From ab8d6d40eb2b775abbcf4c7bafc5570fb8f8b4ed Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 27 Jan 2025 17:55:08 +0200 Subject: [PATCH 01/94] Initial rules. --- .../ruleengine/ConditionalMutation.java | 33 +++++++++++++++++++ .../execution/ruleengine/RequestMutation.java | 8 +++++ .../ruleengine/RequestMutationFactory.java | 8 +++++ 3 files changed, 49 insertions(+) create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java diff --git a/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java b/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java new file mode 100644 index 00000000000..9865a709412 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java @@ -0,0 +1,33 @@ +package org.prebid.server.execution.ruleengine; + +import com.iab.openrtb.request.BidRequest; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +public class ConditionalMutation implements RequestMutation { + + private final Map subrules; + private final Function argumentExtractor; + private final RequestMutation defaultAction; + + public ConditionalMutation(Map subrules, + Function argumentExtractor, + RequestMutation defaultAction) { + + this.subrules = new HashMap<>(subrules); + this.argumentExtractor = Objects.requireNonNull(argumentExtractor); + this.defaultAction = Objects.requireNonNull(defaultAction); + } + + @Override + public BidRequest mutate(BidRequest request) { + return Optional.ofNullable(argumentExtractor.apply(request)) + .map(subrules::get) + .orElse(defaultAction) + .mutate(request); + } +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java new file mode 100644 index 00000000000..f3bf12bec74 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java @@ -0,0 +1,8 @@ +package org.prebid.server.execution.ruleengine; + +import com.iab.openrtb.request.BidRequest; + +public interface RequestMutation { + + public BidRequest mutate(BidRequest request); +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java new file mode 100644 index 00000000000..104d756d7c9 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java @@ -0,0 +1,8 @@ +package org.prebid.server.execution.ruleengine; + +public class RequestMutationFactory { + + public RequestMutation create(String rule) { + + } +} From b230ab86bcd35f3cc1014938b3a7ac4163d3884e Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 28 Jan 2025 05:16:52 +0200 Subject: [PATCH 02/94] POC. --- .../java/org/prebid/server/Application.java | 49 +++++++++- .../ruleengine/ConditionalMutation.java | 23 +++-- .../server/execution/ruleengine/Mutation.java | 6 ++ .../execution/ruleengine/MutationFactory.java | 97 +++++++++++++++++++ .../execution/ruleengine/RequestMutation.java | 8 -- .../ruleengine/RequestMutationFactory.java | 8 -- .../extractors/ArgumentExtractor.java | 8 ++ .../extractors/BooleanExtractor.java | 21 ++++ .../extractors/IntegerExtractor.java | 23 +++++ .../extractors/StringExtractor.java | 21 ++++ 10 files changed, 234 insertions(+), 30 deletions(-) create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/Mutation.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java create mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java diff --git a/src/main/java/org/prebid/server/Application.java b/src/main/java/org/prebid/server/Application.java index 2490d95a89b..185c5a52cd3 100644 --- a/src/main/java/org/prebid/server/Application.java +++ b/src/main/java/org/prebid/server/Application.java @@ -1,13 +1,58 @@ package org.prebid.server; -import org.springframework.boot.SpringApplication; +import com.fasterxml.jackson.databind.node.TextNode; +import lombok.Builder; +import lombok.Value; +import org.prebid.server.execution.ruleengine.Mutation; +import org.prebid.server.execution.ruleengine.MutationFactory; +import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; +import org.prebid.server.execution.ruleengine.extractors.BooleanExtractor; +import org.prebid.server.execution.ruleengine.extractors.IntegerExtractor; +import org.prebid.server.execution.ruleengine.extractors.StringExtractor; import org.springframework.boot.autoconfigure.SpringBootApplication; +import java.util.List; +import java.util.Map; + @SuppressWarnings("checkstyle:hideutilityclassconstructor") @SpringBootApplication public class Application { + @Value + @Builder(toBuilder = true) + private static class Test { + String name; + + int version; + + boolean test; + } + + private static final String BOOL_EXTR = "boolean-extractor"; + private static final String INT_EXTR = "int-extractor"; + private static final String STR_EXTR = "string-extractor"; + public static void main(String[] args) { - SpringApplication.run(Application.class, args); + final Map> argumentExtractors = + Map.of(BOOL_EXTR, BooleanExtractor.of(test -> test.test), + INT_EXTR, IntegerExtractor.of(test -> test.version), + STR_EXTR, StringExtractor.of(test -> test.name)); + + final MutationFactory mutationFactory = new MutationFactory<>( + argumentExtractors, + node -> test -> test.toBuilder().name(node.textValue()).build()); + + final Mutation mutation = mutationFactory.parse( + List.of(BOOL_EXTR, BOOL_EXTR, INT_EXTR, STR_EXTR), + Map.of( + "true|true|123|hello", TextNode.valueOf("1"), + "true|true|5|test", TextNode.valueOf("2"), + "false|false|123|bruh", TextNode.valueOf("3"), + "true|false|1|hello", TextNode.valueOf("4"), + "true|true|-2|aloha", TextNode.valueOf("5"), + "true|false|-2|hello", TextNode.valueOf("6"), + "true|true|123|last", TextNode.valueOf("7"))); + + final Test mutated = mutation.mutate(new Test("bruh", 123, false)); } } diff --git a/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java b/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java index 9865a709412..cf86103b7f8 100644 --- a/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java +++ b/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java @@ -1,22 +1,21 @@ package org.prebid.server.execution.ruleengine; -import com.iab.openrtb.request.BidRequest; +import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; -public class ConditionalMutation implements RequestMutation { +public class ConditionalMutation implements Mutation { - private final Map subrules; - private final Function argumentExtractor; - private final RequestMutation defaultAction; + private final Map> subrules; + private final ArgumentExtractor argumentExtractor; + private final Mutation defaultAction; - public ConditionalMutation(Map subrules, - Function argumentExtractor, - RequestMutation defaultAction) { + public ConditionalMutation(Map> subrules, + ArgumentExtractor argumentExtractor, + Mutation defaultAction) { this.subrules = new HashMap<>(subrules); this.argumentExtractor = Objects.requireNonNull(argumentExtractor); @@ -24,10 +23,10 @@ public ConditionalMutation(Map subrules, } @Override - public BidRequest mutate(BidRequest request) { - return Optional.ofNullable(argumentExtractor.apply(request)) + public T mutate(T input) { + return Optional.ofNullable(argumentExtractor.extract(input)) .map(subrules::get) .orElse(defaultAction) - .mutate(request); + .mutate(input); } } diff --git a/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java b/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java new file mode 100644 index 00000000000..a7fc3ea66c6 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java @@ -0,0 +1,6 @@ +package org.prebid.server.execution.ruleengine; + +public interface Mutation { + + T mutate(T value); +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java new file mode 100644 index 00000000000..b30b7e2a71b --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java @@ -0,0 +1,97 @@ +package org.prebid.server.execution.ruleengine; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class MutationFactory { + + private final Map> argumentExtractors; + private final Function> mutationParser; + + public MutationFactory(Map> argumentExtractors, + Function> mutationParser) { + + this.argumentExtractors = new HashMap<>(argumentExtractors); + this.mutationParser = Objects.requireNonNull(mutationParser); + } + + public Mutation parse(List argumentSchema, Map mutationRules) { + if (argumentSchema.isEmpty()) { + return input -> input; + } + + if (!argumentSchema.stream().allMatch(argumentExtractors::containsKey)) { + throw new IllegalArgumentException("Invalid arguments schema"); + } + + final List> subrules = mutationRules.entrySet().stream() + .map(entry -> Pair.of( + List.of(StringUtils.defaultString(entry.getKey()).split("\\|")), + mutationParser.apply(entry.getValue()))) + .map(entry -> new MutationSubrule<>(entry.getKey(), entry.getValue())) + .toList(); + + return parseConditionalMutation(argumentSchema, subrules); + } + + private Mutation parseConditionalMutation(List argumentSchema, + List> mutationSubrules) { + + final ArgumentExtractor argumentExtractor = + (ArgumentExtractor) argumentExtractors.get(argumentSchema.getFirst()); + + if (argumentSchema.size() == 1) { + final Map> parsedSubrules = mutationSubrules.stream() + .collect(Collectors.toMap( + subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()), + MutationSubrule::mutation, + (left, right) -> left, + HashMap::new)); + + return new ConditionalMutation<>(parsedSubrules, argumentExtractor, input -> input); + } + + // a | b | c | d + // a | e | 1 | 2 + // a | * | * | * + final Map>> subrules = + mutationSubrules.stream() + .collect(Collectors.groupingBy( + subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()))); + + final Map> parsedSubrules = subrules.entrySet().stream() + .map(entry -> Pair.of( + entry.getKey(), + entry.getValue().stream() + .map(MutationSubrule::tail) + .toList())) + + .map(entry -> Pair.of( + entry.getKey(), + parseConditionalMutation(tail(argumentSchema), entry.getValue()))) + + .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + + return new ConditionalMutation<>(parsedSubrules, argumentExtractor, input -> input); + } + + private static List tail(List list) { + return list.subList(1, list.size()); + } + + private record MutationSubrule(List argumentMatchers, Mutation mutation) { + public MutationSubrule tail() { + return new MutationSubrule<>(MutationFactory.tail(argumentMatchers), mutation); + } + } +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java deleted file mode 100644 index f3bf12bec74..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutation.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.prebid.server.execution.ruleengine; - -import com.iab.openrtb.request.BidRequest; - -public interface RequestMutation { - - public BidRequest mutate(BidRequest request); -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java deleted file mode 100644 index 104d756d7c9..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/RequestMutationFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.prebid.server.execution.ruleengine; - -public class RequestMutationFactory { - - public RequestMutation create(String rule) { - - } -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java new file mode 100644 index 00000000000..237e2a5fdde --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java @@ -0,0 +1,8 @@ +package org.prebid.server.execution.ruleengine.extractors; + +public interface ArgumentExtractor { + + R extract(T input); + + R extract(String input); +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java new file mode 100644 index 00000000000..dc86071a2ac --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java @@ -0,0 +1,21 @@ +package org.prebid.server.execution.ruleengine.extractors; + +import lombok.Value; + +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class BooleanExtractor implements ArgumentExtractor { + + Function delegate; + + @Override + public Boolean extract(T input) { + return delegate.apply(input); + } + + @Override + public Boolean extract(String input) { + return Boolean.parseBoolean(input); + } +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java new file mode 100644 index 00000000000..071fddfc731 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java @@ -0,0 +1,23 @@ +package org.prebid.server.execution.ruleengine.extractors; + +import lombok.Value; +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class IntegerExtractor implements ArgumentExtractor { + + Function delegate; + + @Override + public Integer extract(T input) { + return delegate.apply(input); + } + + @Override + public Integer extract(String input) { + return NumberUtils.toInt(input); + } + +} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java new file mode 100644 index 00000000000..553dc398dd4 --- /dev/null +++ b/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java @@ -0,0 +1,21 @@ +package org.prebid.server.execution.ruleengine.extractors; + +import lombok.Value; + +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class StringExtractor implements ArgumentExtractor { + + Function delegate; + + @Override + public String extract(T input) { + return delegate.apply(input); + } + + @Override + public String extract(String input) { + return input; + } +} From 4d34955fa48b7f12d5d6dd6186a931baa633265b Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 28 Jan 2025 05:29:37 +0200 Subject: [PATCH 03/94] POC refactoring. --- src/main/java/org/prebid/server/Application.java | 16 +++++++++------- .../execution/ruleengine/MutationFactory.java | 3 ++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/prebid/server/Application.java b/src/main/java/org/prebid/server/Application.java index 185c5a52cd3..d3bb6963c19 100644 --- a/src/main/java/org/prebid/server/Application.java +++ b/src/main/java/org/prebid/server/Application.java @@ -21,6 +21,7 @@ public class Application { @Value @Builder(toBuilder = true) private static class Test { + String name; int version; @@ -28,22 +29,22 @@ private static class Test { boolean test; } - private static final String BOOL_EXTR = "boolean-extractor"; - private static final String INT_EXTR = "int-extractor"; - private static final String STR_EXTR = "string-extractor"; + private static final String NAME_EXTRACTOR = "name-extractor"; + private static final String VERSION_EXTRACTOR = "version-extractor"; + private static final String IS_TEST_EXTRACTOR = "is-test-extractor"; public static void main(String[] args) { final Map> argumentExtractors = - Map.of(BOOL_EXTR, BooleanExtractor.of(test -> test.test), - INT_EXTR, IntegerExtractor.of(test -> test.version), - STR_EXTR, StringExtractor.of(test -> test.name)); + Map.of(NAME_EXTRACTOR, BooleanExtractor.of(test -> test.test), + VERSION_EXTRACTOR, IntegerExtractor.of(test -> test.version), + IS_TEST_EXTRACTOR, StringExtractor.of(test -> test.name)); final MutationFactory mutationFactory = new MutationFactory<>( argumentExtractors, node -> test -> test.toBuilder().name(node.textValue()).build()); final Mutation mutation = mutationFactory.parse( - List.of(BOOL_EXTR, BOOL_EXTR, INT_EXTR, STR_EXTR), + List.of(NAME_EXTRACTOR, NAME_EXTRACTOR, VERSION_EXTRACTOR, IS_TEST_EXTRACTOR), Map.of( "true|true|123|hello", TextNode.valueOf("1"), "true|true|5|test", TextNode.valueOf("2"), @@ -54,5 +55,6 @@ public static void main(String[] args) { "true|true|123|last", TextNode.valueOf("7"))); final Test mutated = mutation.mutate(new Test("bruh", 123, false)); + System.out.println(mutated); } } diff --git a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java index b30b7e2a71b..d8202d3d24a 100644 --- a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java +++ b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java @@ -1,7 +1,6 @@ package org.prebid.server.execution.ruleengine; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; @@ -52,6 +51,7 @@ private Mutation parseConditionalMutation(List argumentSchema, if (argumentSchema.size() == 1) { final Map> parsedSubrules = mutationSubrules.stream() + // todo: add filter for * .collect(Collectors.toMap( subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()), MutationSubrule::mutation, @@ -66,6 +66,7 @@ private Mutation parseConditionalMutation(List argumentSchema, // a | * | * | * final Map>> subrules = mutationSubrules.stream() + // todo: add filter for * .collect(Collectors.groupingBy( subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()))); From af8c16342f89e1eeaec31e989c1878c21f16beb0 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 29 Jan 2025 13:59:14 +0200 Subject: [PATCH 04/94] POC refactoring. --- .../java/org/prebid/server/Application.java | 35 ++++++---------- .../execution/ruleengine/MutationFactory.java | 41 +++++-------------- 2 files changed, 23 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/prebid/server/Application.java b/src/main/java/org/prebid/server/Application.java index d3bb6963c19..cedfc050795 100644 --- a/src/main/java/org/prebid/server/Application.java +++ b/src/main/java/org/prebid/server/Application.java @@ -1,11 +1,9 @@ package org.prebid.server; -import com.fasterxml.jackson.databind.node.TextNode; import lombok.Builder; import lombok.Value; import org.prebid.server.execution.ruleengine.Mutation; import org.prebid.server.execution.ruleengine.MutationFactory; -import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; import org.prebid.server.execution.ruleengine.extractors.BooleanExtractor; import org.prebid.server.execution.ruleengine.extractors.IntegerExtractor; import org.prebid.server.execution.ruleengine.extractors.StringExtractor; @@ -29,30 +27,21 @@ private static class Test { boolean test; } - private static final String NAME_EXTRACTOR = "name-extractor"; - private static final String VERSION_EXTRACTOR = "version-extractor"; - private static final String IS_TEST_EXTRACTOR = "is-test-extractor"; - public static void main(String[] args) { - final Map> argumentExtractors = - Map.of(NAME_EXTRACTOR, BooleanExtractor.of(test -> test.test), - VERSION_EXTRACTOR, IntegerExtractor.of(test -> test.version), - IS_TEST_EXTRACTOR, StringExtractor.of(test -> test.name)); - - final MutationFactory mutationFactory = new MutationFactory<>( - argumentExtractors, - node -> test -> test.toBuilder().name(node.textValue()).build()); + final BooleanExtractor isTestExtractor = BooleanExtractor.of(test -> test.test); + final IntegerExtractor versionExtractor = IntegerExtractor.of(test -> test.version); + final StringExtractor nameExtractor = StringExtractor.of(test -> test.name); - final Mutation mutation = mutationFactory.parse( - List.of(NAME_EXTRACTOR, NAME_EXTRACTOR, VERSION_EXTRACTOR, IS_TEST_EXTRACTOR), + final Mutation mutation = new MutationFactory().buildMutation( + List.of(isTestExtractor, isTestExtractor, versionExtractor, nameExtractor), Map.of( - "true|true|123|hello", TextNode.valueOf("1"), - "true|true|5|test", TextNode.valueOf("2"), - "false|false|123|bruh", TextNode.valueOf("3"), - "true|false|1|hello", TextNode.valueOf("4"), - "true|true|-2|aloha", TextNode.valueOf("5"), - "true|false|-2|hello", TextNode.valueOf("6"), - "true|true|123|last", TextNode.valueOf("7"))); + "true|true|123|hello", test -> test.toBuilder().name("1").build(), + "true|true|5|test", test -> test.toBuilder().name("2").build(), + "false|false|123|bruh", test -> test.toBuilder().name("3").build(), + "true|false|1|hello", test -> test.toBuilder().name("4").build(), + "true|true|-2|aloha", test -> test.toBuilder().name("5").build(), + "true|false|-2|hello", test -> test.toBuilder().name("6").build(), + "true|true|123|last", test -> test.toBuilder().name("7").build())); final Test mutated = mutation.mutate(new Test("bruh", 123, false)); System.out.println(mutated); diff --git a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java index d8202d3d24a..38ac3da660c 100644 --- a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java +++ b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java @@ -1,62 +1,42 @@ package org.prebid.server.execution.ruleengine; -import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.function.Function; import java.util.stream.Collectors; public class MutationFactory { - private final Map> argumentExtractors; - private final Function> mutationParser; - - public MutationFactory(Map> argumentExtractors, - Function> mutationParser) { - - this.argumentExtractors = new HashMap<>(argumentExtractors); - this.mutationParser = Objects.requireNonNull(mutationParser); - } - - public Mutation parse(List argumentSchema, Map mutationRules) { - if (argumentSchema.isEmpty()) { + public Mutation buildMutation(List> extractors, Map> rules) { + if (extractors.isEmpty()) { return input -> input; } - if (!argumentSchema.stream().allMatch(argumentExtractors::containsKey)) { - throw new IllegalArgumentException("Invalid arguments schema"); - } - - final List> subrules = mutationRules.entrySet().stream() + final List> subrules = rules.entrySet().stream() .map(entry -> Pair.of( List.of(StringUtils.defaultString(entry.getKey()).split("\\|")), - mutationParser.apply(entry.getValue()))) + entry.getValue())) .map(entry -> new MutationSubrule<>(entry.getKey(), entry.getValue())) .toList(); - return parseConditionalMutation(argumentSchema, subrules); + return parseConditionalMutation(extractors, subrules); } - private Mutation parseConditionalMutation(List argumentSchema, + private Mutation parseConditionalMutation(List> argumentExtractors, List> mutationSubrules) { final ArgumentExtractor argumentExtractor = - (ArgumentExtractor) argumentExtractors.get(argumentSchema.getFirst()); + (ArgumentExtractor) argumentExtractors.getFirst(); - if (argumentSchema.size() == 1) { + if (argumentExtractors.size() == 1) { final Map> parsedSubrules = mutationSubrules.stream() // todo: add filter for * .collect(Collectors.toMap( subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()), - MutationSubrule::mutation, - (left, right) -> left, - HashMap::new)); + MutationSubrule::mutation)); return new ConditionalMutation<>(parsedSubrules, argumentExtractor, input -> input); } @@ -79,7 +59,7 @@ private Mutation parseConditionalMutation(List argumentSchema, .map(entry -> Pair.of( entry.getKey(), - parseConditionalMutation(tail(argumentSchema), entry.getValue()))) + parseConditionalMutation(tail(argumentExtractors), entry.getValue()))) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); @@ -91,6 +71,7 @@ private static List tail(List list) { } private record MutationSubrule(List argumentMatchers, Mutation mutation) { + public MutationSubrule tail() { return new MutationSubrule<>(MutationFactory.tail(argumentMatchers), mutation); } From e64d6fdfe55f5a4c46c097f3fbf6e4708d336d84 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 29 Jan 2025 15:13:20 +0200 Subject: [PATCH 05/94] POC refactoring, added wildcard matching. --- .../java/org/prebid/server/Application.java | 16 +-- .../execution/ruleengine/MutationFactory.java | 100 ++++++++++-------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/prebid/server/Application.java b/src/main/java/org/prebid/server/Application.java index cedfc050795..15376545665 100644 --- a/src/main/java/org/prebid/server/Application.java +++ b/src/main/java/org/prebid/server/Application.java @@ -33,15 +33,15 @@ public static void main(String[] args) { final StringExtractor nameExtractor = StringExtractor.of(test -> test.name); final Mutation mutation = new MutationFactory().buildMutation( - List.of(isTestExtractor, isTestExtractor, versionExtractor, nameExtractor), + List.of(isTestExtractor, versionExtractor, nameExtractor), Map.of( - "true|true|123|hello", test -> test.toBuilder().name("1").build(), - "true|true|5|test", test -> test.toBuilder().name("2").build(), - "false|false|123|bruh", test -> test.toBuilder().name("3").build(), - "true|false|1|hello", test -> test.toBuilder().name("4").build(), - "true|true|-2|aloha", test -> test.toBuilder().name("5").build(), - "true|false|-2|hello", test -> test.toBuilder().name("6").build(), - "true|true|123|last", test -> test.toBuilder().name("7").build())); + "false|12|hello", test -> test.toBuilder().name("1").build(), + "true|5|test", test -> test.toBuilder().name("2").build(), + "false|12|*", test -> test.toBuilder().name("3").build(), + "true|1|hello", test -> test.toBuilder().name("4").build(), + "true|-2|aloha", test -> test.toBuilder().name("5").build(), + "true|-2|hello", test -> test.toBuilder().name("6").build(), + "false|*|*", test -> test.toBuilder().name("7").build())); final Test mutated = mutation.mutate(new Test("bruh", 123, false)); System.out.println(mutated); diff --git a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java index 38ac3da660c..eba8e190562 100644 --- a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java +++ b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java @@ -1,79 +1,93 @@ package org.prebid.server.execution.ruleengine; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; public class MutationFactory { + private static final String DEFAULT_ARGUMENT_MATCHER = "*"; + + private final Mutation identityMutation = input -> input; + public Mutation buildMutation(List> extractors, Map> rules) { if (extractors.isEmpty()) { - return input -> input; + return identityMutation; } - final List> subrules = rules.entrySet().stream() - .map(entry -> Pair.of( - List.of(StringUtils.defaultString(entry.getKey()).split("\\|")), - entry.getValue())) - .map(entry -> new MutationSubrule<>(entry.getKey(), entry.getValue())) - .toList(); + return parseConditionalMutation(extractors, toParsingContexts(extractors, rules)); + } + + private List> toParsingContexts(List> extractors, + Map> rules) { + + final List> contexts = new ArrayList<>(); + + for (Map.Entry> entry : rules.entrySet()) { + final List arguments = List.of(StringUtils.defaultString(entry.getKey()).split("\\|")); + final Mutation mutation = entry.getValue(); - return parseConditionalMutation(extractors, subrules); + if (extractors.size() != arguments.size()) { + throw new IllegalArgumentException("Argument count mismatch"); + } + + contexts.add(new ParsingContext<>(arguments, mutation)); + } + + return contexts; } private Mutation parseConditionalMutation(List> argumentExtractors, - List> mutationSubrules) { + List> parsingContexts) { + + if (argumentExtractors.isEmpty()) { + if (parsingContexts.size() > 1) { + throw new IllegalArgumentException("Ambiguous matcher rules"); + } + + return parsingContexts.getFirst().mutation(); + } final ArgumentExtractor argumentExtractor = (ArgumentExtractor) argumentExtractors.getFirst(); + final List> nextArgumentExtractors = tail(argumentExtractors); - if (argumentExtractors.size() == 1) { - final Map> parsedSubrules = mutationSubrules.stream() - // todo: add filter for * - .collect(Collectors.toMap( - subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()), - MutationSubrule::mutation)); + final Map>> subrules = parsingContexts.stream() + .collect(Collectors.groupingBy( + ParsingContext::argumentMatcher, + Collectors.mapping(ParsingContext::next, Collectors.toList()))); - return new ConditionalMutation<>(parsedSubrules, argumentExtractor, input -> input); - } + final Mutation defaultAction = Optional.ofNullable(subrules.get(DEFAULT_ARGUMENT_MATCHER)) + .map(subrule -> parseConditionalMutation(nextArgumentExtractors, subrule)) + .orElse(identityMutation); - // a | b | c | d - // a | e | 1 | 2 - // a | * | * | * - final Map>> subrules = - mutationSubrules.stream() - // todo: add filter for * - .collect(Collectors.groupingBy( - subrule -> argumentExtractor.extract(subrule.argumentMatchers().getFirst()))); + subrules.remove(DEFAULT_ARGUMENT_MATCHER); final Map> parsedSubrules = subrules.entrySet().stream() - .map(entry -> Pair.of( - entry.getKey(), - entry.getValue().stream() - .map(MutationSubrule::tail) - .toList())) + .collect(Collectors.toMap( + entry -> argumentExtractor.extract(entry.getKey()), + entry -> parseConditionalMutation(nextArgumentExtractors, entry.getValue()))); - .map(entry -> Pair.of( - entry.getKey(), - parseConditionalMutation(tail(argumentExtractors), entry.getValue()))) + return new ConditionalMutation<>(parsedSubrules, argumentExtractor, defaultAction); + } - .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + private record ParsingContext(List argumentMatchers, Mutation mutation) { + + public ParsingContext next() { + return new ParsingContext<>(tail(argumentMatchers), mutation); + } - return new ConditionalMutation<>(parsedSubrules, argumentExtractor, input -> input); + public String argumentMatcher() { + return argumentMatchers.getFirst(); + } } private static List tail(List list) { return list.subList(1, list.size()); } - - private record MutationSubrule(List argumentMatchers, Mutation mutation) { - - public MutationSubrule tail() { - return new MutationSubrule<>(MutationFactory.tail(argumentMatchers), mutation); - } - } } From 21aa1441ac9ef37a9835c2868bc7da69e41d91aa Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 11 Feb 2025 17:08:02 +0200 Subject: [PATCH 06/94] Moved code to module --- extra/modules/pom.xml | 1 + extra/modules/rule-engine/pom.xml | 15 +++ extra/modules/rule-engine/src/lombok.config | 1 + .../config/RuleEngineModuleConfiguration.java | 16 ++++ .../engine/core/rules/ArgumentExtractor.java | 6 ++ .../modules/rule/engine/core/rules/Rule.java | 25 +++++ .../rule/engine/core/rules/RuleFactory.java | 65 +++++++++++++ .../rule/engine/core/rules/RuleNode.java | 17 ++++ .../rule/engine/core/rules/RuleTree.java | 36 +++++++ .../rule/engine/v1/RuleEngineModule.java | 30 ++++++ ...RuleEngineProcessedAuctionRequestHook.java | 92 ++++++++++++++++++ .../java/org/prebid/server/Application.java | 40 +------- .../ruleengine/ConditionalMutation.java | 32 ------- .../server/execution/ruleengine/Mutation.java | 6 -- .../execution/ruleengine/MutationFactory.java | 93 ------------------- .../extractors/ArgumentExtractor.java | 8 -- .../extractors/BooleanExtractor.java | 21 ----- .../extractors/IntegerExtractor.java | 23 ----- .../extractors/StringExtractor.java | 21 ----- 19 files changed, 306 insertions(+), 242 deletions(-) create mode 100644 extra/modules/rule-engine/pom.xml create mode 100644 extra/modules/rule-engine/src/lombok.config create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/Mutation.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java delete mode 100644 src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml index 8f558dec877..93cdf2b97d4 100644 --- a/extra/modules/pom.xml +++ b/extra/modules/pom.xml @@ -24,6 +24,7 @@ pb-response-correction greenbids-real-time-data pb-request-correction + rule-engine diff --git a/extra/modules/rule-engine/pom.xml b/extra/modules/rule-engine/pom.xml new file mode 100644 index 00000000000..c539d0649f1 --- /dev/null +++ b/extra/modules/rule-engine/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.prebid.server.hooks.modules + all-modules + 3.18.0-SNAPSHOT + + + rule-engine + + rule-engine + Rule engine module + diff --git a/extra/modules/rule-engine/src/lombok.config b/extra/modules/rule-engine/src/lombok.config new file mode 100644 index 00000000000..efd92714219 --- /dev/null +++ b/extra/modules/rule-engine/src/lombok.config @@ -0,0 +1 @@ +lombok.anyConstructor.addConstructorProperties = true diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java new file mode 100644 index 00000000000..c0e87cc4f18 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -0,0 +1,16 @@ +package org.prebid.server.hooks.modules.rule.engine.config; + +import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +//@ConditionalOnProperty(prefix = "hooks." + RuleEngineModule.CODE, name = "enabled", havingValue = "true") +public class RuleEngineModuleConfiguration { + + @Bean + RuleEngineModule ruleEngineModule() { + return new RuleEngineModule(); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java new file mode 100644 index 00000000000..c42452c207d --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java @@ -0,0 +1,6 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +public interface ArgumentExtractor { + + String extract(T value); +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java new file mode 100644 index 00000000000..03cff8fbeea --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java @@ -0,0 +1,25 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +public class Rule { + + private final RuleTree ruleTree; + private final List> argumentExtractors; + + public Rule(RuleTree ruleTree, List> argumentExtractors) { + this.ruleTree = Objects.requireNonNull(ruleTree); + this.argumentExtractors = Objects.requireNonNull(argumentExtractors); + } + + public Function apply(R value) { + final List args = argumentExtractors.stream() + .map(extractor -> extractor.extract(value)) + .toList(); + + return ruleTree.getAction(args); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java new file mode 100644 index 00000000000..78ed156e454 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java @@ -0,0 +1,65 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class RuleFactory { + + public Rule buildRule(List> argumentExtractors, + Map> rules) { + + return new Rule<>(new RuleTree<>(parseRuleNode(toParsingContexts(rules))), argumentExtractors); + } + + private List> toParsingContexts(Map> rules) { + final List> contexts = new ArrayList<>(); + + for (Map.Entry> entry : rules.entrySet()) { + final List arguments = List.of(StringUtils.defaultString(entry.getKey()).split("\\|")); + final Function action = entry.getValue(); + + + contexts.add(new ParsingContext<>(arguments, action)); + } + + return contexts; + } + + private RuleNode parseRuleNode(List> parsingContexts) { + if (parsingContexts.size() == 1 && parsingContexts.getFirst().argumentMatchers().isEmpty()) { + return new RuleNode.LeafNode<>(parsingContexts.getFirst().action); + } + + final Map>> subrules = parsingContexts.stream() + .collect(Collectors.groupingBy( + ParsingContext::argumentMatcher, + Collectors.mapping(ParsingContext::next, Collectors.toList()))); + + final Map> parsedSubrules = subrules.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> parseRuleNode(entry.getValue()))); + + return new RuleNode.IntermediateNode<>(parsedSubrules); + } + + private record ParsingContext(List argumentMatchers, Function action) { + + public ParsingContext next() { + return new ParsingContext<>(tail(argumentMatchers), action); + } + + public String argumentMatcher() { + return argumentMatchers.getFirst(); + } + } + + private static List tail(List list) { + return list.subList(1, list.size()); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java new file mode 100644 index 00000000000..034b687efdb --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java @@ -0,0 +1,17 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import java.util.Map; +import java.util.function.Function; + +public sealed interface RuleNode { + + record IntermediateNode(Map> children) implements RuleNode { + + public RuleNode next(String arg) { + return children.get(arg); + } + } + + record LeafNode(Function action) implements RuleNode { + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java new file mode 100644 index 00000000000..90512c6c501 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java @@ -0,0 +1,36 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.apache.commons.lang3.ObjectUtils; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +public class RuleTree { + + RuleNode root; + + public RuleTree(RuleNode root) { + this.root = Objects.requireNonNull(root); + } + + public Function getAction(List args) { + RuleNode next = root; + + for (String arg : args) { + next = switch (next) { + case RuleNode.LeafNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); + case RuleNode.IntermediateNode node -> ObjectUtils.firstNonNull(node.next(arg), node.next("*")); + case null -> throw new IllegalArgumentException("Action absent"); + }; + } + + return switch (next) { + case RuleNode.LeafNode leaf -> leaf.action(); + case RuleNode.IntermediateNode ignored -> + throw new IllegalArgumentException("Argument count mismatch"); + case null -> throw new IllegalArgumentException("Action absent"); + }; + } + +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java new file mode 100644 index 00000000000..931afb77530 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java @@ -0,0 +1,30 @@ +package org.prebid.server.hooks.modules.rule.engine.v1; + +import org.prebid.server.hooks.v1.Hook; +import org.prebid.server.hooks.v1.InvocationContext; +import org.prebid.server.hooks.v1.Module; + +import java.util.Collection; +import java.util.Collections; + +public class RuleEngineModule implements Module { + + public static final String CODE = "rule-engine"; + + private final Collection> hooks; + + public RuleEngineModule() { + this.hooks = Collections.singleton( + new RuleEngineProcessedAuctionRequestHook()); + } + + @Override + public String code() { + return CODE; + } + + @Override + public Collection> hooks() { + return hooks; + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java new file mode 100644 index 00000000000..4b8b44ba626 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -0,0 +1,92 @@ +package org.prebid.server.hooks.modules.rule.engine.v1; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import io.vertx.core.Future; +import lombok.Builder; +import lombok.Value; +import org.prebid.server.hooks.execution.v1.InvocationResultImpl; +import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ArgumentExtractor; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleFactory; +import org.prebid.server.hooks.v1.InvocationAction; +import org.prebid.server.hooks.v1.InvocationResult; +import org.prebid.server.hooks.v1.InvocationStatus; +import org.prebid.server.hooks.v1.PayloadUpdate; +import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; +import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; +import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; + +import java.util.List; +import java.util.Map; + +public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { + + private static final String CODE = "rule-engine-processed-auction-request"; + + @Value + @Builder(toBuilder = true) + private static class Test { + + String name; + + int version; + + boolean test; + } + + @Value + private static class RuleContext { + + BidRequest.BidRequestBuilder bidRequest; + + Imp imp; + } + + @Override + public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { + final BidRequest originalBidRequest = auctionRequestPayload.bidRequest(); + + final ArgumentExtractor isTestExtractor = test -> String.valueOf(test.test); + final ArgumentExtractor versionExtractor = test -> String.valueOf(test.version); + final ArgumentExtractor nameExtractor = test -> test.name; + + final RuleFactory ruleFactory = new RuleFactory<>(); + + final Rule rule = ruleFactory.buildRule( + List.of(isTestExtractor, versionExtractor, nameExtractor), + Map.of( + "false|12|hello", test -> test, + "true|5|test", test -> test, + "false|12|*", test -> test.toBuilder().id("id").build(), + "true|1|hello", test -> test, + "true|-2|aloha", test -> test, + "true|-2|hello", test -> test, + "false|*|*", test -> test)); + + final Imp mutated = rule + .apply(new Test("bruh", 12, false)) + .apply(Imp.builder().build()); + + System.out.println(mutated); + + return Future.succeededFuture(succeeded(payload -> + AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder() + .ext(originalBidRequest.getExt()) + .build()))); + } + + public static InvocationResult succeeded(PayloadUpdate payloadUpdate) { + return InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(InvocationAction.update) + .payloadUpdate(payloadUpdate) + .build(); + } + + @Override + public String code() { + return CODE; + } +} diff --git a/src/main/java/org/prebid/server/Application.java b/src/main/java/org/prebid/server/Application.java index 15376545665..2490d95a89b 100644 --- a/src/main/java/org/prebid/server/Application.java +++ b/src/main/java/org/prebid/server/Application.java @@ -1,49 +1,13 @@ package org.prebid.server; -import lombok.Builder; -import lombok.Value; -import org.prebid.server.execution.ruleengine.Mutation; -import org.prebid.server.execution.ruleengine.MutationFactory; -import org.prebid.server.execution.ruleengine.extractors.BooleanExtractor; -import org.prebid.server.execution.ruleengine.extractors.IntegerExtractor; -import org.prebid.server.execution.ruleengine.extractors.StringExtractor; +import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import java.util.List; -import java.util.Map; - @SuppressWarnings("checkstyle:hideutilityclassconstructor") @SpringBootApplication public class Application { - @Value - @Builder(toBuilder = true) - private static class Test { - - String name; - - int version; - - boolean test; - } - public static void main(String[] args) { - final BooleanExtractor isTestExtractor = BooleanExtractor.of(test -> test.test); - final IntegerExtractor versionExtractor = IntegerExtractor.of(test -> test.version); - final StringExtractor nameExtractor = StringExtractor.of(test -> test.name); - - final Mutation mutation = new MutationFactory().buildMutation( - List.of(isTestExtractor, versionExtractor, nameExtractor), - Map.of( - "false|12|hello", test -> test.toBuilder().name("1").build(), - "true|5|test", test -> test.toBuilder().name("2").build(), - "false|12|*", test -> test.toBuilder().name("3").build(), - "true|1|hello", test -> test.toBuilder().name("4").build(), - "true|-2|aloha", test -> test.toBuilder().name("5").build(), - "true|-2|hello", test -> test.toBuilder().name("6").build(), - "false|*|*", test -> test.toBuilder().name("7").build())); - - final Test mutated = mutation.mutate(new Test("bruh", 123, false)); - System.out.println(mutated); + SpringApplication.run(Application.class, args); } } diff --git a/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java b/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java deleted file mode 100644 index cf86103b7f8..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/ConditionalMutation.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.prebid.server.execution.ruleengine; - -import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -public class ConditionalMutation implements Mutation { - - private final Map> subrules; - private final ArgumentExtractor argumentExtractor; - private final Mutation defaultAction; - - public ConditionalMutation(Map> subrules, - ArgumentExtractor argumentExtractor, - Mutation defaultAction) { - - this.subrules = new HashMap<>(subrules); - this.argumentExtractor = Objects.requireNonNull(argumentExtractor); - this.defaultAction = Objects.requireNonNull(defaultAction); - } - - @Override - public T mutate(T input) { - return Optional.ofNullable(argumentExtractor.extract(input)) - .map(subrules::get) - .orElse(defaultAction) - .mutate(input); - } -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java b/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java deleted file mode 100644 index a7fc3ea66c6..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/Mutation.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.prebid.server.execution.ruleengine; - -public interface Mutation { - - T mutate(T value); -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java b/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java deleted file mode 100644 index eba8e190562..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/MutationFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.prebid.server.execution.ruleengine; - -import org.apache.commons.lang3.StringUtils; -import org.prebid.server.execution.ruleengine.extractors.ArgumentExtractor; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -public class MutationFactory { - - private static final String DEFAULT_ARGUMENT_MATCHER = "*"; - - private final Mutation identityMutation = input -> input; - - public Mutation buildMutation(List> extractors, Map> rules) { - if (extractors.isEmpty()) { - return identityMutation; - } - - return parseConditionalMutation(extractors, toParsingContexts(extractors, rules)); - } - - private List> toParsingContexts(List> extractors, - Map> rules) { - - final List> contexts = new ArrayList<>(); - - for (Map.Entry> entry : rules.entrySet()) { - final List arguments = List.of(StringUtils.defaultString(entry.getKey()).split("\\|")); - final Mutation mutation = entry.getValue(); - - if (extractors.size() != arguments.size()) { - throw new IllegalArgumentException("Argument count mismatch"); - } - - contexts.add(new ParsingContext<>(arguments, mutation)); - } - - return contexts; - } - - private Mutation parseConditionalMutation(List> argumentExtractors, - List> parsingContexts) { - - if (argumentExtractors.isEmpty()) { - if (parsingContexts.size() > 1) { - throw new IllegalArgumentException("Ambiguous matcher rules"); - } - - return parsingContexts.getFirst().mutation(); - } - - final ArgumentExtractor argumentExtractor = - (ArgumentExtractor) argumentExtractors.getFirst(); - final List> nextArgumentExtractors = tail(argumentExtractors); - - final Map>> subrules = parsingContexts.stream() - .collect(Collectors.groupingBy( - ParsingContext::argumentMatcher, - Collectors.mapping(ParsingContext::next, Collectors.toList()))); - - final Mutation defaultAction = Optional.ofNullable(subrules.get(DEFAULT_ARGUMENT_MATCHER)) - .map(subrule -> parseConditionalMutation(nextArgumentExtractors, subrule)) - .orElse(identityMutation); - - subrules.remove(DEFAULT_ARGUMENT_MATCHER); - - final Map> parsedSubrules = subrules.entrySet().stream() - .collect(Collectors.toMap( - entry -> argumentExtractor.extract(entry.getKey()), - entry -> parseConditionalMutation(nextArgumentExtractors, entry.getValue()))); - - return new ConditionalMutation<>(parsedSubrules, argumentExtractor, defaultAction); - } - - private record ParsingContext(List argumentMatchers, Mutation mutation) { - - public ParsingContext next() { - return new ParsingContext<>(tail(argumentMatchers), mutation); - } - - public String argumentMatcher() { - return argumentMatchers.getFirst(); - } - } - - private static List tail(List list) { - return list.subList(1, list.size()); - } -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java deleted file mode 100644 index 237e2a5fdde..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/extractors/ArgumentExtractor.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.prebid.server.execution.ruleengine.extractors; - -public interface ArgumentExtractor { - - R extract(T input); - - R extract(String input); -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java deleted file mode 100644 index dc86071a2ac..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/extractors/BooleanExtractor.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.prebid.server.execution.ruleengine.extractors; - -import lombok.Value; - -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class BooleanExtractor implements ArgumentExtractor { - - Function delegate; - - @Override - public Boolean extract(T input) { - return delegate.apply(input); - } - - @Override - public Boolean extract(String input) { - return Boolean.parseBoolean(input); - } -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java deleted file mode 100644 index 071fddfc731..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/extractors/IntegerExtractor.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.prebid.server.execution.ruleengine.extractors; - -import lombok.Value; -import org.apache.commons.lang3.math.NumberUtils; - -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class IntegerExtractor implements ArgumentExtractor { - - Function delegate; - - @Override - public Integer extract(T input) { - return delegate.apply(input); - } - - @Override - public Integer extract(String input) { - return NumberUtils.toInt(input); - } - -} diff --git a/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java b/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java deleted file mode 100644 index 553dc398dd4..00000000000 --- a/src/main/java/org/prebid/server/execution/ruleengine/extractors/StringExtractor.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.prebid.server.execution.ruleengine.extractors; - -import lombok.Value; - -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class StringExtractor implements ArgumentExtractor { - - Function delegate; - - @Override - public String extract(T input) { - return delegate.apply(input); - } - - @Override - public String extract(String input) { - return input; - } -} From e2cbe61cd6e72285831fc9a6b2549e440f65ba67 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 26 Feb 2025 16:26:17 +0200 Subject: [PATCH 07/94] WIP --- .../rule/engine/core/AccountConfigReader.java | 14 +++++++++++ .../rule/engine/core/cache/RulesCache.java | 17 ++++++++++++++ .../core/config/model/AccountConfig.java | 18 +++++++++++++++ .../core/config/model/ModelGroupConfig.java | 22 ++++++++++++++++++ .../engine/core/config/model/RuleConfig.java | 13 +++++++++++ .../core/config/model/RuleFunctionConfig.java | 12 ++++++++++ .../core/config/model/RuleSetConfig.java | 20 ++++++++++++++++ .../rule/engine/core/request/ModelGroup.java | 9 ++++++++ .../core/request/RequestModelGroup.java | 8 +++++++ .../rule/engine/core/request/RequestRule.java | 15 ++++++++++++ .../core/request/RequestRuleContext.java | 15 ++++++++++++ .../core/request/RequestRuleResult.java | 12 ++++++++++ .../rule/engine/core/request/RuleSet.java | 9 ++++++++ ...RuleEngineProcessedAuctionRequestHook.java | 23 ++++++++++--------- 14 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java new file mode 100644 index 00000000000..d8d7be68f5c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core; + +import com.iab.openrtb.request.Imp; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; + +public class AccountConfigReader { + + public Rule parse(AccountConfig config) { + + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java new file mode 100644 index 00000000000..2a9b837350c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java @@ -0,0 +1,17 @@ +package org.prebid.server.hooks.modules.rule.engine.core.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.iab.openrtb.request.Imp; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; + +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +public class RulesCache { + + final ConcurrentMap> cache = Caffeine.newBuilder() + .expireAfterAccess(100, TimeUnit.HOURS) + .maximumSize(10000) + .>build() + .asMap(); +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java new file mode 100644 index 00000000000..5ea5ef8cc1e --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java @@ -0,0 +1,18 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +import java.time.Instant; +import java.util.List; + +@Value(staticConstructor = "of") +public class AccountConfig { + + boolean enabled; + + Instant timestamp; + + @JsonProperty("ruleSets") + List ruleSets; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java new file mode 100644 index 00000000000..b3ca146360c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -0,0 +1,22 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class ModelGroupConfig { + + int weight; + + @JsonProperty("analyticsKey") + String analyticsKey; + + String version; + + @JsonProperty("default") + RuleFunctionConfig defaultAction; + + List rules; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java new file mode 100644 index 00000000000..d1b513c8c67 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java @@ -0,0 +1,13 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class RuleConfig { + + List conditions; + + List results; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java new file mode 100644 index 00000000000..28f2a9826f6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java @@ -0,0 +1,12 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; + +@Value(staticConstructor = "of") +public class RuleFunctionConfig { + + String function; + + JsonNode args; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java new file mode 100644 index 00000000000..e8aa507182c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java @@ -0,0 +1,20 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; +import org.prebid.server.hooks.execution.model.Stage; + +import java.util.List; + +@Value(staticConstructor = "of") +public class RuleSetConfig { + + Stage stage; + + String name; + + String version; + + @JsonProperty("modelGroups") + List modelGroups; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java new file mode 100644 index 00000000000..c3a47d515ab --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java @@ -0,0 +1,9 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class ModelGroup { + + +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java new file mode 100644 index 00000000000..2033a2b2477 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +public class RequestModelGroup { + + String model; + + +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java new file mode 100644 index 00000000000..2d731b0165e --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java @@ -0,0 +1,15 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class RequestRule { + + String name; + + String version; + + List ruleSets; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java new file mode 100644 index 00000000000..6f99ee34574 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java @@ -0,0 +1,15 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import lombok.Value; + +@Value(staticConstructor = "of") +public class RequestRuleContext { + + boolean validation; + + Imp imp; + + BidRequest bidRequest; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java new file mode 100644 index 00000000000..8d0409a3964 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java @@ -0,0 +1,12 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.Imp; +import lombok.Value; + +@Value(staticConstructor = "of") +public class RequestRuleResult { + + Imp adjustedImp; + + +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java new file mode 100644 index 00000000000..9c0a27cd453 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java @@ -0,0 +1,9 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class RuleSet { + + +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 4b8b44ba626..94756f80b28 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -5,8 +5,12 @@ import io.vertx.core.Future; import lombok.Builder; import lombok.Value; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.ArgumentExtractor; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleFactory; @@ -47,23 +51,19 @@ private static class RuleContext { @Override public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { final BidRequest originalBidRequest = auctionRequestPayload.bidRequest(); + final List imps = ListUtils.emptyIfNull(originalBidRequest.getImp()); + final RuleFactory ruleFactory = new RuleFactory<>(); + final Rule rule = null; + + for (Imp imp : imps) { + RequestRuleResult result = rule.apply(RequestRuleContext.of(false, imp, originalBidRequest)); + } final ArgumentExtractor isTestExtractor = test -> String.valueOf(test.test); final ArgumentExtractor versionExtractor = test -> String.valueOf(test.version); final ArgumentExtractor nameExtractor = test -> test.name; - final RuleFactory ruleFactory = new RuleFactory<>(); - final Rule rule = ruleFactory.buildRule( - List.of(isTestExtractor, versionExtractor, nameExtractor), - Map.of( - "false|12|hello", test -> test, - "true|5|test", test -> test, - "false|12|*", test -> test.toBuilder().id("id").build(), - "true|1|hello", test -> test, - "true|-2|aloha", test -> test, - "true|-2|hello", test -> test, - "false|*|*", test -> test)); final Imp mutated = rule .apply(new Test("bruh", 12, false)) @@ -82,6 +82,7 @@ public static InvocationResult succeeded(PayloadUpdate Date: Thu, 3 Apr 2025 18:09:25 +0300 Subject: [PATCH 08/94] WIP. --- .../core/request/RequestRuleResult.java | 12 ---- .../engine/core/rules/ArgumentExtractor.java | 6 -- .../modules/rule/engine/core/rules/Rule.java | 21 +++---- .../rule/engine/core/rules/RuleFactory.java | 34 +++++------ .../rule/engine/core/rules/RuleNode.java | 8 +-- .../rule/engine/core/rules/RuleTree.java | 22 +++---- .../core/rules/request/RequestRule.java | 48 +++++++++++++++ .../core/rules/request/RequestRuleResult.java | 4 ++ .../core/rules/request/RequestSchema.java | 30 ++++++++++ .../core/rules/result/ResultFunction.java | 6 ++ .../rules/result/ResultFunctionArguments.java | 17 ++++++ .../rules/result/ResultFunctionHolder.java | 15 +++++ .../rule/engine/core/rules/schema/Schema.java | 11 ++++ .../core/rules/schema/SchemaFunction.java | 6 ++ .../rules/schema/SchemaFunctionArguments.java | 19 ++++++ .../rules/schema/SchemaFunctionHolder.java | 14 +++++ ...RuleEngineProcessedAuctionRequestHook.java | 59 +++++++------------ 17 files changed, 232 insertions(+), 100 deletions(-) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java deleted file mode 100644 index 8d0409a3964..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -import com.iab.openrtb.request.Imp; -import lombok.Value; - -@Value(staticConstructor = "of") -public class RequestRuleResult { - - Imp adjustedImp; - - -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java deleted file mode 100644 index c42452c207d..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ArgumentExtractor.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules; - -public interface ArgumentExtractor { - - String extract(T value); -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java index 03cff8fbeea..0d69d50ce6a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java @@ -1,25 +1,26 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; + import java.util.List; import java.util.Objects; -import java.util.function.Function; -public class Rule { +public class Rule { - private final RuleTree ruleTree; - private final List> argumentExtractors; + private final RuleTree ruleTree; + private final List> schemaFunctions; - public Rule(RuleTree ruleTree, List> argumentExtractors) { + public Rule(RuleTree ruleTree, List> schemaFunctions) { this.ruleTree = Objects.requireNonNull(ruleTree); - this.argumentExtractors = Objects.requireNonNull(argumentExtractors); + this.schemaFunctions = Objects.requireNonNull(schemaFunctions); } - public Function apply(R value) { - final List args = argumentExtractors.stream() - .map(extractor -> extractor.extract(value)) + public TO apply(FROM value, CONTEXT context) { + final List args = schemaFunctions.stream() + .map(extractor -> extractor.extract(context)) .toList(); - return ruleTree.getAction(args); + return ruleTree.getAction(args).apply(context); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java index 78ed156e454..468bc510aa7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java @@ -1,46 +1,44 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.function.Function; import java.util.stream.Collectors; -public class RuleFactory { +public class RuleFactory { - public Rule buildRule(List> argumentExtractors, - Map> rules) { - - return new Rule<>(new RuleTree<>(parseRuleNode(toParsingContexts(rules))), argumentExtractors); + public static RuleTree buildTree(Map rules) { + return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); } - private List> toParsingContexts(Map> rules) { - final List> contexts = new ArrayList<>(); + private static List> toParsingContexts(Map rules) { + final List> contexts = new ArrayList<>(); - for (Map.Entry> entry : rules.entrySet()) { + for (Map.Entry entry : rules.entrySet()) { final List arguments = List.of(StringUtils.defaultString(entry.getKey()).split("\\|")); - final Function action = entry.getValue(); + final T value = entry.getValue(); - contexts.add(new ParsingContext<>(arguments, action)); + contexts.add(new ParsingContext<>(arguments, value)); } return contexts; } - private RuleNode parseRuleNode(List> parsingContexts) { + private static RuleNode parseRuleNode(List> parsingContexts) { if (parsingContexts.size() == 1 && parsingContexts.getFirst().argumentMatchers().isEmpty()) { - return new RuleNode.LeafNode<>(parsingContexts.getFirst().action); + return new RuleNode.LeafNode<>(parsingContexts.getFirst().value); } - final Map>> subrules = parsingContexts.stream() + final Map>> subrules = parsingContexts.stream() .collect(Collectors.groupingBy( ParsingContext::argumentMatcher, Collectors.mapping(ParsingContext::next, Collectors.toList()))); - final Map> parsedSubrules = subrules.entrySet().stream() + final Map> parsedSubrules = subrules.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> parseRuleNode(entry.getValue()))); @@ -48,10 +46,10 @@ private RuleNode parseRuleNode(List> parsingContexts) return new RuleNode.IntermediateNode<>(parsedSubrules); } - private record ParsingContext(List argumentMatchers, Function action) { + private record ParsingContext(List argumentMatchers, T value) { - public ParsingContext next() { - return new ParsingContext<>(tail(argumentMatchers), action); + public ParsingContext next() { + return new ParsingContext<>(tail(argumentMatchers), value); } public String argumentMatcher() { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java index 034b687efdb..ad83cddf2f4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java @@ -3,15 +3,15 @@ import java.util.Map; import java.util.function.Function; -public sealed interface RuleNode { +public sealed interface RuleNode { - record IntermediateNode(Map> children) implements RuleNode { + record IntermediateNode(Map> children) implements RuleNode { - public RuleNode next(String arg) { + public RuleNode next(String arg) { return children.get(arg); } } - record LeafNode(Function action) implements RuleNode { + record LeafNode(T value) implements RuleNode { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java index 90512c6c501..40661f3cf33 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java @@ -6,31 +6,31 @@ import java.util.Objects; import java.util.function.Function; -public class RuleTree { +public class RuleTree { - RuleNode root; + RuleNode root; - public RuleTree(RuleNode root) { + public RuleTree(RuleNode root) { this.root = Objects.requireNonNull(root); } - public Function getAction(List args) { - RuleNode next = root; + public T getValue(List path) { + RuleNode next = root; - for (String arg : args) { + for (String pathPart : path) { next = switch (next) { - case RuleNode.LeafNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); - case RuleNode.IntermediateNode node -> ObjectUtils.firstNonNull(node.next(arg), node.next("*")); + case RuleNode.LeafNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); + case RuleNode.IntermediateNode node -> + ObjectUtils.firstNonNull(node.next(pathPart), node.next("*")); case null -> throw new IllegalArgumentException("Action absent"); }; } return switch (next) { - case RuleNode.LeafNode leaf -> leaf.action(); - case RuleNode.IntermediateNode ignored -> + case RuleNode.LeafNode leaf -> leaf.value(); + case RuleNode.IntermediateNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); case null -> throw new IllegalArgumentException("Action absent"); }; } - } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java new file mode 100644 index 00000000000..cabc6055ae4 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java @@ -0,0 +1,48 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.ListUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class RequestRule { + + private static final Map> SCHEMA_FUNCTIONS = Map.of( + "deviceCountry", RequestSchema::deviceCountryExtractor); + private static final Map> RESULT_FUNCTIONS = Map.of(); + + private final List> schemaFunctionHolders; + private final RuleTree> ruleTree; + + public RequestRule(List> schemaFunctionHolders, + RuleTree> ruleTree) { + + this.schemaFunctionHolders = Objects.requireNonNull(schemaFunctionHolders); + this.ruleTree = Objects.requireNonNull(ruleTree); + } + + public RequestRuleResult process(BidRequest bidRequest) { + final List imps = ListUtils.emptyIfNull(bidRequest.getImp()); + + + for (Imp imp : imps) { + final List schema = schemaFunctionHolders.stream() + .map(holder -> holder.getSchemaFunction().extract( + SchemaFunctionArguments.of( + bidRequest, holder.getArguments(), false, imp))) + .toList(); + + final ResultFunctionHolder result = ruleTree.getValue(schema); + return result.getFunction().apply(result.getArguments()); + } + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java new file mode 100644 index 00000000000..86f72432337 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java @@ -0,0 +1,4 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +public class RequestRuleResult { +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java new file mode 100644 index 00000000000..0dabc671a69 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -0,0 +1,30 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +import java.util.List; +import java.util.Optional; + +public class RequestSchema { + + private RequestSchema() { + } + + public static String deviceCountryExtractor(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand(); + final List args = arguments.getArgs(); + if (CollectionUtils.isEmpty(args)) { + return Optional.ofNullable(bidRequest.getDevice()) + .map(Device::getGeo) + .map(Geo::getCountry) + .orElse(null); + } + + return "true"; + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java new file mode 100644 index 00000000000..5934082e426 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -0,0 +1,6 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; + +import java.util.function.Function; + +public interface ResultFunction extends Function { +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java new file mode 100644 index 00000000000..77e4ee11dbf --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java @@ -0,0 +1,17 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class ResultFunctionArguments { + + List bidders; + + Integer seatNonBid; + + boolean ifSyncedId; + + String analyticsValue; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java new file mode 100644 index 00000000000..8f80db4ffbf --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java @@ -0,0 +1,15 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class ResultFunctionHolder { + + String rule; + + ResultFunction, R> function; + + List arguments; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java new file mode 100644 index 00000000000..4331becd369 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -0,0 +1,11 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class Schema { + + List> schemaFunctions; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java new file mode 100644 index 00000000000..01cb4d7deed --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -0,0 +1,6 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +public interface SchemaFunction { + + String extract(SchemaFunctionArguments arguments); +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java new file mode 100644 index 00000000000..4e5839eed27 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -0,0 +1,19 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.Imp; +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class SchemaFunctionArguments { + + T operand; + + List args; + + boolean validation; + + Imp imp; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java new file mode 100644 index 00000000000..f9f4fe4f32d --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class SchemaFunctionHolder { + + SchemaFunction schemaFunction; + + List arguments; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 94756f80b28..54a10e43a43 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -5,25 +5,19 @@ import io.vertx.core.Future; import lombok.Builder; import lombok.Value; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.ListUtils; -import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.ArgumentExtractor; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleFactory; -import org.prebid.server.hooks.v1.InvocationAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.v1.InvocationResult; -import org.prebid.server.hooks.v1.InvocationStatus; -import org.prebid.server.hooks.v1.PayloadUpdate; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; +import java.util.ArrayList; import java.util.List; -import java.util.Map; public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -40,30 +34,26 @@ private static class Test { boolean test; } - @Value - private static class RuleContext { - - BidRequest.BidRequestBuilder bidRequest; - - Imp imp; - } - @Override - public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { + public Future> call(AuctionRequestPayload auctionRequestPayload, + AuctionInvocationContext invocationContext) { + final BidRequest originalBidRequest = auctionRequestPayload.bidRequest(); - final List imps = ListUtils.emptyIfNull(originalBidRequest.getImp()); - final RuleFactory ruleFactory = new RuleFactory<>(); - final Rule rule = null; + final BidRequest bidRequest = auctionRequestPayload.bidRequest(); - for (Imp imp : imps) { - RequestRuleResult result = rule.apply(RequestRuleContext.of(false, imp, originalBidRequest)); - } - final ArgumentExtractor isTestExtractor = test -> String.valueOf(test.test); - final ArgumentExtractor versionExtractor = test -> String.valueOf(test.version); - final ArgumentExtractor nameExtractor = test -> test.name; + final RuleTree> ruleTree = null; + final List> schemaFunctions = new ArrayList<>(); + for (Imp imp : imps) { + final List schema = schemaFunctions.stream() + .map(holder -> holder.getSchemaFunction().extract( + SchemaFunctionArguments.of( + bidRequest, holder.getArguments(), false, imp))) + .toList(); + final ResultFunctionHolder result = ruleTree.getValue(schema); + } final Imp mutated = rule .apply(new Test("bruh", 12, false)) @@ -77,15 +67,6 @@ public Future> call(AuctionRequestPayloa .build()))); } - public static InvocationResult succeeded(PayloadUpdate payloadUpdate) { - return InvocationResultImpl.builder() - .status(InvocationStatus.success) - .action(InvocationAction.update) - .payloadUpdate(payloadUpdate) - .analyticsTags() - .build(); - } - @Override public String code() { return CODE; From 90575818ff71a538e4ae47221ea08682839c4d36 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 16 Apr 2025 23:24:35 +0300 Subject: [PATCH 09/94] WIP. --- .../rule/engine/core/AccountConfigReader.java | 14 ---- .../modules/rule/engine/core/RuleParser.java | 12 +++ .../rule/engine/core/rules/RuleFactory.java | 4 +- .../core/rules/request/BasicRequestRule.java | 73 +++++++++++++++++++ .../core/rules/request/RequestRule.java | 44 +---------- .../core/rules/request/RequestRuleResult.java | 10 +++ .../core/rules/request/RequestSchema.java | 10 +++ .../functions/IncludeBiddersFunction.java | 24 ++++++ .../core/rules/result/ResultFunction.java | 9 ++- .../rules/result/ResultFunctionResult.java | 13 ++++ ...ultFunctionHolder.java => RuleResult.java} | 7 +- .../ExcludeBiddersArguments.java} | 4 +- .../arguments/IncludeBiddersArguments.java | 17 +++++ .../arguments/InfrastructureArguments.java | 19 +++++ .../result/arguments/LogATagArguments.java | 9 +++ .../arguments/ResultFunctionArguments.java | 5 ++ .../rule/engine/core/rules/schema/Schema.java | 2 +- .../rules/schema/SchemaFunctionHolder.java | 2 + ...RuleEngineProcessedAuctionRequestHook.java | 48 ++---------- 19 files changed, 220 insertions(+), 106 deletions(-) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/{ResultFunctionHolder.java => RuleResult.java} (52%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/{ResultFunctionArguments.java => arguments/ExcludeBiddersArguments.java} (73%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java deleted file mode 100644 index d8d7be68f5c..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/AccountConfigReader.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core; - -import com.iab.openrtb.request.Imp; -import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; - -public class AccountConfigReader { - - public Rule parse(AccountConfig config) { - - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java new file mode 100644 index 00000000000..ed5f5fea5e9 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java @@ -0,0 +1,12 @@ +package org.prebid.server.hooks.modules.rule.engine.core; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.BasicRequestRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRule; + +public class RuleParser { + + public RequestRule parse(ObjectNode accountConfig) { + return new BasicRequestRule(null, null); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java index 468bc510aa7..f9ef736b5bc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleResult; import java.util.ArrayList; import java.util.List; @@ -10,7 +10,7 @@ public class RuleFactory { - public static RuleTree buildTree(Map rules) { + public static RuleTree buildTree(Map rules) { return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java new file mode 100644 index 00000000000..e8fa501b98f --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java @@ -0,0 +1,73 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.ListUtils; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.model.UpdateResult; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BasicRequestRule implements RequestRule { + + private final Schema schema; + private final RuleTree> ruleTree; + + public BasicRequestRule(Schema schema, RuleTree> ruleTree) { + this.schema = Objects.requireNonNull(schema); + this.ruleTree = Objects.requireNonNull(ruleTree); + } + + @Override + public RequestRuleResult process(BidRequest bidRequest, boolean validation) { + final List imps = ListUtils.emptyIfNull(bidRequest.getImp()); + final List> schemaFunctionHolders = schema.getFunctions(); + + final List updatedImps = new ArrayList<>(); + final List activities = new ArrayList<>(); + boolean updated = false; + + for (Imp imp : imps) { + final List matchers = schemaFunctionHolders.stream() + .map(holder -> holder.getSchemaFunction().extract( + SchemaFunctionArguments.of( + bidRequest, holder.getArguments(), false, imp))) + .toList(); + + final RuleResult ruleResult = ruleTree.getValue(matchers); + final ResultFunction action = ruleResult.getAction(); + final InfrastructureArguments infrastructureArguments = null; + + Imp updatedImp = imp; + for (ResultFunctionArguments arguments : ruleResult.getArguments()) { + final ResultFunctionResult impResult = action.apply( + arguments, infrastructureArguments, updatedImp); + final UpdateResult updateResult = impResult.getUpdateResult(); + + updated = updateResult.isUpdated() || updated; + updatedImp = updateResult.getValue(); + activities.addAll(impResult.getAnalyticsTags().activities()); + } + + updatedImps.add(updatedImp); + } + + final UpdateResult result = updated + ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) + : UpdateResult.unaltered(bidRequest); + + return RequestRuleResult.of(result, TagsImpl.of(activities)); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java index cabc6055ae4..4b7148ccbaf 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java @@ -1,48 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; -import org.apache.commons.collections4.ListUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; -import java.util.List; -import java.util.Map; -import java.util.Objects; +public interface RequestRule { -public class RequestRule { - - private static final Map> SCHEMA_FUNCTIONS = Map.of( - "deviceCountry", RequestSchema::deviceCountryExtractor); - private static final Map> RESULT_FUNCTIONS = Map.of(); - - private final List> schemaFunctionHolders; - private final RuleTree> ruleTree; - - public RequestRule(List> schemaFunctionHolders, - RuleTree> ruleTree) { - - this.schemaFunctionHolders = Objects.requireNonNull(schemaFunctionHolders); - this.ruleTree = Objects.requireNonNull(ruleTree); - } - - public RequestRuleResult process(BidRequest bidRequest) { - final List imps = ListUtils.emptyIfNull(bidRequest.getImp()); - - - for (Imp imp : imps) { - final List schema = schemaFunctionHolders.stream() - .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of( - bidRequest, holder.getArguments(), false, imp))) - .toList(); - - final ResultFunctionHolder result = ruleTree.getValue(schema); - return result.getFunction().apply(result.getArguments()); - } - } + RequestRuleResult process(BidRequest bidRequest, boolean validation); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java index 86f72432337..a33c3539242 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java @@ -1,4 +1,14 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request; +import com.iab.openrtb.request.BidRequest; +import lombok.Value; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +@Value(staticConstructor = "of") public class RequestRuleResult { + + UpdateResult bidRequest; + + Tags analyticsTags; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index 0dabc671a69..8600595e18e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -5,9 +5,11 @@ import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import java.util.List; +import java.util.Map; import java.util.Optional; public class RequestSchema { @@ -15,6 +17,13 @@ public class RequestSchema { private RequestSchema() { } + public static final String DEVICE_COUNTRY_FUNCTION = "deviceCountry"; + public static final String AD_UNIT_CODE_FUNCTION = "adUnitCode"; + public static final String MEDIA_TYPE_FUNCTION = "mediaType"; + + private static final Map> SCHEMA_FUNCTIONS = Map.of( + AD_UNIT_CODE_FUNCTION, RequestSchema::deviceCountryExtractor); + public static String deviceCountryExtractor(SchemaFunctionArguments arguments) { final BidRequest bidRequest = arguments.getOperand(); final List args = arguments.getArgs(); @@ -27,4 +36,5 @@ public static String deviceCountryExtractor(SchemaFunctionArguments return "true"; } + } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java new file mode 100644 index 00000000000..b218fd9ddef --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java @@ -0,0 +1,24 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions; + +import com.iab.openrtb.request.Imp; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; +import org.prebid.server.model.UpdateResult; + +import java.util.Collections; + +public class IncludeBiddersFunction implements ResultFunction { + + + @Override + public ResultFunctionResult apply(ResultFunctionArguments arguments, + InfrastructureArguments infrastructureArguments, + Imp operand) { + + + return ResultFunctionResult.of(UpdateResult.unaltered(operand), TagsImpl.of(Collections.emptyList())); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index 5934082e426..96a5c6a088d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -1,6 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; -import java.util.function.Function; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; -public interface ResultFunction extends Function { +public interface ResultFunction { + + ResultFunctionResult apply(ResultFunctionArguments arguments, + InfrastructureArguments infrastructureArguments, + T operand); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java new file mode 100644 index 00000000000..db473536266 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java @@ -0,0 +1,13 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; + +import lombok.Value; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +@Value(staticConstructor = "of") +public class ResultFunctionResult { + + UpdateResult updateResult; + + Tags analyticsTags; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java similarity index 52% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java index 8f80db4ffbf..95336b9e011 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java @@ -1,15 +1,16 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; import java.util.List; @Value(staticConstructor = "of") -public class ResultFunctionHolder { +public class RuleResult { - String rule; + String ruleFired; - ResultFunction, R> function; + ResultFunction action; List arguments; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java similarity index 73% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java index 77e4ee11dbf..2dc1c6623d7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java @@ -1,11 +1,11 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result; +package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; import lombok.Value; import java.util.List; @Value(staticConstructor = "of") -public class ResultFunctionArguments { +public class ExcludeBiddersArguments implements ResultFunctionArguments { List bidders; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java new file mode 100644 index 00000000000..eb301047c24 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java @@ -0,0 +1,17 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class IncludeBiddersArguments implements ResultFunctionArguments { + + List bidders; + + Integer seatNonBid; + + boolean ifSyncedId; + + String analyticsValue; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java new file mode 100644 index 00000000000..5b85e3be7ff --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java @@ -0,0 +1,19 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; + +import lombok.Value; + +import java.util.Map; + +@Value(staticConstructor = "of") +public class InfrastructureArguments { + + Map schemaFunctionResults; + + String analyticsKey; + + String ruleFired; + + String modelVersion; + + boolean validation; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java new file mode 100644 index 00000000000..97eb4127bc7 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java @@ -0,0 +1,9 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class LogATagArguments implements ResultFunctionArguments { + + String analyticsValue; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java new file mode 100644 index 00000000000..d3050ccdf2c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java @@ -0,0 +1,5 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; + +public sealed interface ResultFunctionArguments permits + ExcludeBiddersArguments, IncludeBiddersArguments, LogATagArguments { +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java index 4331becd369..9bfa3804d9b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -7,5 +7,5 @@ @Value(staticConstructor = "of") public class Schema { - List> schemaFunctions; + List> functions; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java index f9f4fe4f32d..f074eb1659c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -8,6 +8,8 @@ @Value(staticConstructor = "of") public class SchemaFunctionHolder { + String name; + SchemaFunction schemaFunction; List arguments; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 54a10e43a43..f59c985c208 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -1,65 +1,33 @@ package org.prebid.server.hooks.modules.rule.engine.v1; -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; import io.vertx.core.Future; -import lombok.Builder; -import lombok.Value; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.RuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.v1.InvocationResult; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { private static final String CODE = "rule-engine-processed-auction-request"; - @Value - @Builder(toBuilder = true) - private static class Test { + private final RuleParser ruleParser; - String name; - - int version; - - boolean test; + public RuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { + this.ruleParser = Objects.requireNonNull(ruleParser); } @Override public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { - final BidRequest originalBidRequest = auctionRequestPayload.bidRequest(); - - final BidRequest bidRequest = auctionRequestPayload.bidRequest(); - - final RuleTree> ruleTree = null; - final List> schemaFunctions = new ArrayList<>(); - - for (Imp imp : imps) { - final List schema = schemaFunctions.stream() - .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of( - bidRequest, holder.getArguments(), false, imp))) - .toList(); - - final ResultFunctionHolder result = ruleTree.getValue(schema); - } - - final Imp mutated = rule - .apply(new Test("bruh", 12, false)) - .apply(Imp.builder().build()); - - System.out.println(mutated); + final RequestRule rule = ruleParser.parse(invocationContext.accountConfig()); + final RequestRuleResult result = rule.process(auctionRequestPayload.bidRequest(), false); return Future.succeededFuture(succeeded(payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder() From 7230f79298bde4c7512a755101760b1a6c9316c1 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 23 Apr 2025 02:52:23 +0300 Subject: [PATCH 10/94] Added weighted rule and utilities for it. --- .../modules/rule/engine/core/RuleParser.java | 12 ---- .../core/config/model/ModelGroupConfig.java | 2 +- ...{RuleFactory.java => RuleTreeFactory.java} | 8 +-- .../core/rules/request/BasicRequestRule.java | 3 +- .../rules/request/CompositeRequestRule.java | 32 ++++++++++ .../core/rules/request/RequestRule.java | 2 +- .../core/rules/request/RequestRuleParser.java | 47 +++++++++++++++ .../rules/request/WeightedRequestRule.java | 23 ++++++++ .../core/rules/result/ResultFunction.java | 2 + .../arguments/InfrastructureArguments.java | 2 - .../core/rules/schema/SchemaFunction.java | 2 + .../rules/schema/SchemaFunctionArguments.java | 2 - .../rule/engine/core/util/WeightedEntry.java | 26 ++++++++ .../rule/engine/core/util/WeightedList.java | 59 +++++++++++++++++++ ...RuleEngineProcessedAuctionRequestHook.java | 8 +-- 15 files changed, 201 insertions(+), 29 deletions(-) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{RuleFactory.java => RuleTreeFactory.java} (88%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java deleted file mode 100644 index ed5f5fea5e9..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/RuleParser.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.BasicRequestRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRule; - -public class RuleParser { - - public RequestRule parse(ObjectNode accountConfig) { - return new BasicRequestRule(null, null); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index b3ca146360c..f183f55bdca 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -8,7 +8,7 @@ @Value(staticConstructor = "of") public class ModelGroupConfig { - int weight; + double weight; @JsonProperty("analyticsKey") String analyticsKey; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java similarity index 88% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java index f9ef736b5bc..9551bec2358 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java @@ -8,9 +8,9 @@ import java.util.Map; import java.util.stream.Collectors; -public class RuleFactory { +public class RuleTreeFactory { - public static RuleTree buildTree(Map rules) { + public static RuleTree> buildTree(Map> rules) { return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); } @@ -39,9 +39,7 @@ private static RuleNode parseRuleNode(List> parsingCont Collectors.mapping(ParsingContext::next, Collectors.toList()))); final Map> parsedSubrules = subrules.entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - entry -> parseRuleNode(entry.getValue()))); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> parseRuleNode(entry.getValue()))); return new RuleNode.IntermediateNode<>(parsedSubrules); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java index e8fa501b98f..048663e0a14 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java @@ -42,8 +42,7 @@ public RequestRuleResult process(BidRequest bidRequest, boolean validation) { for (Imp imp : imps) { final List matchers = schemaFunctionHolders.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of( - bidRequest, holder.getArguments(), false, imp))) + SchemaFunctionArguments.of(bidRequest, holder.getArguments(), imp))) .toList(); final RuleResult ruleResult = ruleTree.getValue(matchers); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java new file mode 100644 index 00000000000..f730e09e49f --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java @@ -0,0 +1,32 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.iab.openrtb.request.BidRequest; +import lombok.Value; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.model.UpdateResult; + +import java.util.ArrayList; +import java.util.List; + +@Value(staticConstructor = "of") +public class CompositeRequestRule implements RequestRule { + + List subrules; + + @Override + public RequestRuleResult process(BidRequest bidRequest) { + final List activities = new ArrayList<>(); + BidRequest modifiedRequest = bidRequest; + boolean updated = false; + + for (RequestRule subrule : subrules) { + final RequestRuleResult subresult = subrule.process(modifiedRequest); + modifiedRequest = subresult.getBidRequest().getValue(); + updated = updated | subresult.getBidRequest().isUpdated(); + activities.addAll(subresult.getAnalyticsTags().activities()); + } + + return RequestRuleResult.of(UpdateResult.of(updated, modifiedRequest), TagsImpl.of(activities)); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java index 4b7148ccbaf..b577bcb8c8e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java @@ -4,5 +4,5 @@ public interface RequestRule { - RequestRuleResult process(BidRequest bidRequest, boolean validation); + RequestRuleResult process(BidRequest bidRequest); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java new file mode 100644 index 00000000000..d3a59d3ee25 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java @@ -0,0 +1,47 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.hooks.execution.model.Stage; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class RequestRuleParser { + + private final ObjectMapper mapper; + + public RequestRuleParser(ObjectMapper mapper) { + this.mapper = Objects.requireNonNull(mapper); + } + + public Map parse(ObjectNode accountConfig) { + final AccountConfig parsedConfig; + try { + parsedConfig = mapper.treeToValue(accountConfig, AccountConfig.class); + } catch (JsonProcessingException e) { + throw new PreBidException(e.getMessage()); + } + + return parsedConfig.getRuleSets().stream() + .map(RequestRuleParser::toRulePerStage) + .map(Map::entrySet) + .flatMap(Set::stream) + .collect(Collectors.groupingBy(Map.Entry::getKey, + Collectors.mapping(Map.Entry::getValue, Collectors.toList()))) + .entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, entry -> CompositeRequestRule.of(entry.getValue()))); + } + + private static Map toRulePerStage(RuleSetConfig ruleSetConfig) { + ruleSetConfig.getStage() + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java new file mode 100644 index 00000000000..3859649e225 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java @@ -0,0 +1,23 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; + +import java.util.Objects; +import java.util.random.RandomGenerator; + +public class WeightedRequestRule implements RequestRule { + + private final RandomGenerator random; + private final WeightedList weightedList; + + public WeightedRequestRule(RandomGenerator random, WeightedList weightedList) { + this.random = Objects.requireNonNull(random); + this.weightedList = Objects.requireNonNull(weightedList); + } + + @Override + public RequestRuleResult process(BidRequest bidRequest) { + return weightedList.getForSeed(random.nextDouble()).process(bidRequest); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index 96a5c6a088d..abe3ecf9913 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -8,4 +8,6 @@ public interface ResultFunction { ResultFunctionResult apply(ResultFunctionArguments arguments, InfrastructureArguments infrastructureArguments, T operand); + + void validateArguments(ResultFunctionArguments arguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java index 5b85e3be7ff..8dfabd5ac53 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java @@ -14,6 +14,4 @@ public class InfrastructureArguments { String ruleFired; String modelVersion; - - boolean validation; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 01cb4d7deed..dd3131abab2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -3,4 +3,6 @@ public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); + + void validate(SchemaFunctionArguments arguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java index 4e5839eed27..8d6a69d2bcd 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -13,7 +13,5 @@ public class SchemaFunctionArguments { List args; - boolean validation; - Imp imp; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java new file mode 100644 index 00000000000..e536a16d6ad --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java @@ -0,0 +1,26 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +import lombok.Getter; + +@Getter +public class WeightedEntry { + + public static final double MAX_WEIGHT = 1.0; + + double weight; + + T value; + + private WeightedEntry(double weight, T value) { + this.weight = weight; + this.value = value; + + if (weight < 0 || weight > MAX_WEIGHT) { + throw new IllegalArgumentException("Weight must be between 0 and 100"); + } + } + + public static WeightedEntry of(double weight, T value) { + return new WeightedEntry<>(weight, value); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java new file mode 100644 index 00000000000..c01fe8fa666 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java @@ -0,0 +1,59 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class WeightedList { + + private static final double EPSILON = 1e-6; + + private final List> entries; + + public WeightedList(List> entries) { + validateEntries(entries); + + this.entries = prepareEntries(entries); + } + + private void validateEntries(List> entries) { + Objects.requireNonNull(entries); + + if (entries.isEmpty()) { + throw new IllegalArgumentException("Weighted list cannot be empty"); + } + + final double sum = entries.stream().mapToDouble(WeightedEntry::getWeight).sum(); + if (Math.abs(sum - WeightedEntry.MAX_WEIGHT) > EPSILON) { + throw new IllegalArgumentException( + "Weighted list weights sum must be equal to " + WeightedEntry.MAX_WEIGHT); + } + } + + private List> prepareEntries(List> entries) { + final List> result = new ArrayList<>(entries.size()); + double cumulativeSum = 0; + + for (WeightedEntry entry : entries) { + cumulativeSum += entry.getWeight(); + result.add(WeightedEntry.of(cumulativeSum, entry.getValue())); + } + + return result; + } + + public T getForSeed(double seed) { + if (seed < 0 || seed > WeightedEntry.MAX_WEIGHT) { + throw new IllegalArgumentException("Seed number must be between 0 and 100"); + } + + for (WeightedEntry entry : entries) { + if (seed <= entry.getWeight()) { + return entry.getValue(); + } + } + + // Should never occur + throw new IllegalStateException("No entry found for seed " + seed); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index f59c985c208..80af54b071c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -2,7 +2,7 @@ import io.vertx.core.Future; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.RuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleParser; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleResult; import org.prebid.server.hooks.v1.InvocationResult; @@ -16,9 +16,9 @@ public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRe private static final String CODE = "rule-engine-processed-auction-request"; - private final RuleParser ruleParser; + private final RequestRuleParser ruleParser; - public RuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { + public RuleEngineProcessedAuctionRequestHook(RequestRuleParser ruleParser) { this.ruleParser = Objects.requireNonNull(ruleParser); } @@ -27,7 +27,7 @@ public Future> call(AuctionRequestPayloa AuctionInvocationContext invocationContext) { final RequestRule rule = ruleParser.parse(invocationContext.accountConfig()); - final RequestRuleResult result = rule.process(auctionRequestPayload.bidRequest(), false); + final RequestRuleResult result = rule.process(auctionRequestPayload.bidRequest()); return Future.succeededFuture(succeeded(payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder() From d87cf58ba27a2d0ef508d4a8af95c750e9944faa Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 29 Apr 2025 16:59:23 +0300 Subject: [PATCH 11/94] WIP. --- .../rule/engine/core/cache/RulesCache.java | 1 - .../core/config/model/ModelGroupConfig.java | 2 + .../core/config/model/RuleFunctionConfig.java | 4 +- .../config/model/SchemaFunctionConfig.java | 14 ++++ .../rule/engine/core/rules/CompositeRule.java | 31 ++++++++ .../modules/rule/engine/core/rules/Rule.java | 24 +----- .../rule/engine/core/rules/RuleConfig.java | 14 ++++ .../rule/engine/core/rules/RuleResult.java | 13 ++++ .../rule/engine/core/rules/WeightedRule.java | 24 ++++++ .../rules/request/CompositeRequestRule.java | 32 -------- ...uestRule.java => MatchingRequestRule.java} | 54 +++++++------ .../core/rules/request/RequestRule.java | 8 -- .../core/rules/request/RequestRuleParser.java | 76 ++++++++++++++++--- .../core/rules/request/RequestRuleResult.java | 14 ---- .../core/rules/request/RequestSchema.java | 11 ++- .../rules/request/WeightedRequestRule.java | 23 ------ .../{RuleResult.java => RuleAction.java} | 6 +- .../core/rules/{ => tree}/RuleNode.java | 3 +- .../core/rules/{ => tree}/RuleTree.java | 3 +- .../rules/{ => tree}/RuleTreeFactory.java | 6 +- ...RuleEngineProcessedAuctionRequestHook.java | 12 ++- 21 files changed, 222 insertions(+), 153 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/{BasicRequestRule.java => MatchingRequestRule.java} (53%) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/{RuleResult.java => RuleAction.java} (78%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{ => tree}/RuleNode.java (76%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{ => tree}/RuleTree.java (91%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{ => tree}/RuleTreeFactory.java (89%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java index 2a9b837350c..f44cd77b975 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java @@ -2,7 +2,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.iab.openrtb.request.Imp; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index f183f55bdca..1435451c2d2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -15,6 +15,8 @@ public class ModelGroupConfig { String version; + List schema; + @JsonProperty("default") RuleFunctionConfig defaultAction; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java index 28f2a9826f6..158411a8127 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java @@ -3,10 +3,12 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; +import java.util.List; + @Value(staticConstructor = "of") public class RuleFunctionConfig { String function; - JsonNode args; + List args; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java new file mode 100644 index 00000000000..c814b0510b6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.model; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class SchemaFunctionConfig { + + String function; + + List args; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java new file mode 100644 index 00000000000..81ad45ea0df --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java @@ -0,0 +1,31 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import lombok.Value; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.model.UpdateResult; + +import java.util.ArrayList; +import java.util.List; + +@Value(staticConstructor = "of") +public class CompositeRule implements Rule { + + List> subrules; + + @Override + public RuleResult process(T value) { + final List activities = new ArrayList<>(); + T modifiedValue = value; + boolean updated = false; + + for (Rule subrule : subrules) { + final RuleResult subresult = subrule.process(modifiedValue); + modifiedValue = subresult.getUpdateResult().getValue(); + updated = updated | subresult.getUpdateResult().isUpdated(); + activities.addAll(subresult.getAnalyticsTags().activities()); + } + + return RuleResult.of(UpdateResult.of(updated, modifiedValue), TagsImpl.of(activities)); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java index 0d69d50ce6a..b14bb23f4fc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java @@ -1,26 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +public interface Rule { -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; - -import java.util.List; -import java.util.Objects; - -public class Rule { - - private final RuleTree ruleTree; - private final List> schemaFunctions; - - public Rule(RuleTree ruleTree, List> schemaFunctions) { - this.ruleTree = Objects.requireNonNull(ruleTree); - this.schemaFunctions = Objects.requireNonNull(schemaFunctions); - } - - public TO apply(FROM value, CONTEXT context) { - final List args = schemaFunctions.stream() - .map(extractor -> extractor.extract(context)) - .toList(); - - return ruleTree.getAction(args).apply(context); - } + RuleResult process(T value); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java new file mode 100644 index 00000000000..e7346d9eda1 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; + +import java.util.List; + +@Value(staticConstructor = "of") +public class RuleConfig { + + String ruleFired; + + List> actions; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java new file mode 100644 index 00000000000..70fbafcadc6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -0,0 +1,13 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import lombok.Value; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +@Value(staticConstructor = "of") +public class RuleResult { + + UpdateResult updateResult; + + Tags analyticsTags; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java new file mode 100644 index 00000000000..98d491b93cd --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java @@ -0,0 +1,24 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; + +import java.util.Objects; +import java.util.random.RandomGenerator; + +public class WeightedRule implements Rule { + + private final RandomGenerator random; + private final WeightedList> weightedList; + + public WeightedRule(RandomGenerator random, WeightedList> weightedList) { + this.random = Objects.requireNonNull(random); + this.weightedList = Objects.requireNonNull(weightedList); + } + + @Override + public RuleResult process(T value) { + return weightedList.getForSeed(random.nextDouble()).process(value); + + // TODO: catch unmatched rule exception (add it) and try to run default action instead + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java deleted file mode 100644 index f730e09e49f..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/CompositeRequestRule.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; - -import com.iab.openrtb.request.BidRequest; -import lombok.Value; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.v1.analytics.Activity; -import org.prebid.server.model.UpdateResult; - -import java.util.ArrayList; -import java.util.List; - -@Value(staticConstructor = "of") -public class CompositeRequestRule implements RequestRule { - - List subrules; - - @Override - public RequestRuleResult process(BidRequest bidRequest) { - final List activities = new ArrayList<>(); - BidRequest modifiedRequest = bidRequest; - boolean updated = false; - - for (RequestRule subrule : subrules) { - final RequestRuleResult subresult = subrule.process(modifiedRequest); - modifiedRequest = subresult.getBidRequest().getValue(); - updated = updated | subresult.getBidRequest().isUpdated(); - activities.addAll(subresult.getAnalyticsTags().activities()); - } - - return RequestRuleResult.of(UpdateResult.of(updated, modifiedRequest), TagsImpl.of(activities)); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java similarity index 53% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java index 048663e0a14..87f042e44eb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/BasicRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java @@ -4,10 +4,13 @@ import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; @@ -20,23 +23,23 @@ import java.util.List; import java.util.Objects; -public class BasicRequestRule implements RequestRule { +public class MatchingRequestRule implements Rule { private final Schema schema; - private final RuleTree> ruleTree; + private final RuleTree> ruleTree; - public BasicRequestRule(Schema schema, RuleTree> ruleTree) { + public MatchingRequestRule(Schema schema, RuleTree> ruleTree) { this.schema = Objects.requireNonNull(schema); this.ruleTree = Objects.requireNonNull(ruleTree); } @Override - public RequestRuleResult process(BidRequest bidRequest, boolean validation) { + public RuleResult process(BidRequest bidRequest) { final List imps = ListUtils.emptyIfNull(bidRequest.getImp()); final List> schemaFunctionHolders = schema.getFunctions(); - final List updatedImps = new ArrayList<>(); final List activities = new ArrayList<>(); + BidRequest updatedBidRequest = bidRequest; boolean updated = false; for (Imp imp : imps) { @@ -45,28 +48,29 @@ public RequestRuleResult process(BidRequest bidRequest, boolean validation) { SchemaFunctionArguments.of(bidRequest, holder.getArguments(), imp))) .toList(); - final RuleResult ruleResult = ruleTree.getValue(matchers); - final ResultFunction action = ruleResult.getAction(); - final InfrastructureArguments infrastructureArguments = null; + final RuleConfig ruleConfig = ruleTree.getValue(matchers); - Imp updatedImp = imp; - for (ResultFunctionArguments arguments : ruleResult.getArguments()) { - final ResultFunctionResult impResult = action.apply( - arguments, infrastructureArguments, updatedImp); - final UpdateResult updateResult = impResult.getUpdateResult(); + for (RuleAction action : ruleConfig.getActions()) { + final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( + null, + "analyticsKey", + ruleConfig.getRuleFired(), + "modelVersion"); - updated = updateResult.isUpdated() || updated; - updatedImp = updateResult.getValue(); - activities.addAll(impResult.getAnalyticsTags().activities()); - } + final ResultFunction function = action.getFunction(); - updatedImps.add(updatedImp); - } + for (ResultFunctionArguments arguments : action.getArguments()) { + final ResultFunctionResult bidRequestResult = function.apply( + arguments, infrastructureArguments, updatedBidRequest); + final UpdateResult updateResult = bidRequestResult.getUpdateResult(); - final UpdateResult result = updated - ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) - : UpdateResult.unaltered(bidRequest); + updated = updateResult.isUpdated() || updated; + updatedBidRequest = updateResult.getValue(); + activities.addAll(bidRequestResult.getAnalyticsTags().activities()); + } + } + } - return RequestRuleResult.of(result, TagsImpl.of(activities)); + return RuleResult.of(UpdateResult.of(updated, updatedBidRequest), TagsImpl.of(activities)); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java deleted file mode 100644 index b577bcb8c8e..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRule.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; - -import com.iab.openrtb.request.BidRequest; - -public interface RequestRule { - - RequestRuleResult process(BidRequest bidRequest); -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java index d3a59d3ee25..eab55f5f8b0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java @@ -3,25 +3,43 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ExcludeBiddersArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedEntry; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; +import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; +import java.util.function.Function; +import java.util.random.RandomGenerator; import java.util.stream.Collectors; public class RequestRuleParser { private final ObjectMapper mapper; + private final RandomGenerator randomGenerator; - public RequestRuleParser(ObjectMapper mapper) { + public RequestRuleParser(ObjectMapper mapper, RandomGenerator randomGenerator) { this.mapper = Objects.requireNonNull(mapper); + this.randomGenerator = Objects.requireNonNull(randomGenerator); } - public Map parse(ObjectNode accountConfig) { + public Map> parse(ObjectNode accountConfig) { final AccountConfig parsedConfig; try { parsedConfig = mapper.treeToValue(accountConfig, AccountConfig.class); @@ -30,18 +48,54 @@ public Map parse(ObjectNode accountConfig) { } return parsedConfig.getRuleSets().stream() - .map(RequestRuleParser::toRulePerStage) - .map(Map::entrySet) - .flatMap(Set::stream) - .collect(Collectors.groupingBy(Map.Entry::getKey, - Collectors.mapping(Map.Entry::getValue, Collectors.toList()))) + .collect(Collectors.groupingBy(RuleSetConfig::getStage, + Collectors.mapping( + ruleSetConfig -> toRule(ruleSetConfig.getModelGroups()), + Collectors.toList()))) .entrySet() .stream() .collect(Collectors.toMap( - Map.Entry::getKey, entry -> CompositeRequestRule.of(entry.getValue()))); + e->e.getKey(), entry -> CompositeRule.of(entry.getValue()))); } - private static Map toRulePerStage(RuleSetConfig ruleSetConfig) { - ruleSetConfig.getStage() + private Rule toRule(List modelGroupConfigs) { + final List>> weightedRules = modelGroupConfigs.stream() + .map(config -> WeightedEntry.of(config.getWeight(), toRule(config))) + .toList(); + + return new WeightedRule(randomGenerator, new WeightedList<>(weightedRules)); + } + + private static Rule toRule(ModelGroupConfig modelGroupConfig) { + final Schema schema = parseSchema(modelGroupConfig); + final Map> rules = modelGroupConfig.getRules().stream() + .map(RequestRuleParser::toRuleResult) + .collect(Collectors.toMap(RuleConfig::getRuleFired, Function.identity())); + final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); + + return new MatchingRequestRule(schema, ruleTree); + } + + private static Schema parseSchema(ModelGroupConfig modelGroupConfig) { + final List> schemaFunctions = modelGroupConfig.getSchema().stream() + .map(config -> SchemaFunctionHolder.of( + config.getFunction(), + RequestSchema.schemaFunctionByName(config.getFunction()), + config.getArgs())) + .toList(); + + return Schema.of(schemaFunctions); + } + + private static RuleConfig toRuleResult(org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleConfig ruleConfig) { + final String ruleFired = String.join("|", ruleConfig.getConditions()); + final List> actions = ruleConfig.getResults().stream() + .map(result -> RuleAction.of( + // TODO: parse result arguments properly + RequestSchema.resultFunctionByName(result.getFunction()), + List.of(ExcludeBiddersArguments.of(List.of(), 203, false, "test")))) + .toList(); + + return RuleConfig.of(ruleFired, actions); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java deleted file mode 100644 index a33c3539242..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleResult.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; - -import com.iab.openrtb.request.BidRequest; -import lombok.Value; -import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; - -@Value(staticConstructor = "of") -public class RequestRuleResult { - - UpdateResult bidRequest; - - Tags analyticsTags; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index 8600595e18e..33fa6b52254 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -4,7 +4,9 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; @@ -24,6 +26,14 @@ private RequestSchema() { private static final Map> SCHEMA_FUNCTIONS = Map.of( AD_UNIT_CODE_FUNCTION, RequestSchema::deviceCountryExtractor); + public static SchemaFunction schemaFunctionByName(String function) { + return SCHEMA_FUNCTIONS.get(function); + } + + public static ResultFunction resultFunctionByName(String function) { + return null; + } + public static String deviceCountryExtractor(SchemaFunctionArguments arguments) { final BidRequest bidRequest = arguments.getOperand(); final List args = arguments.getArgs(); @@ -36,5 +46,4 @@ public static String deviceCountryExtractor(SchemaFunctionArguments return "true"; } - } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java deleted file mode 100644 index 3859649e225..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/WeightedRequestRule.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; - -import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; - -import java.util.Objects; -import java.util.random.RandomGenerator; - -public class WeightedRequestRule implements RequestRule { - - private final RandomGenerator random; - private final WeightedList weightedList; - - public WeightedRequestRule(RandomGenerator random, WeightedList weightedList) { - this.random = Objects.requireNonNull(random); - this.weightedList = Objects.requireNonNull(weightedList); - } - - @Override - public RequestRuleResult process(BidRequest bidRequest) { - return weightedList.getForSeed(random.nextDouble()).process(bidRequest); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java similarity index 78% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java index 95336b9e011..740a240e3b6 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java @@ -6,11 +6,9 @@ import java.util.List; @Value(staticConstructor = "of") -public class RuleResult { +public class RuleAction { - String ruleFired; - - ResultFunction action; + ResultFunction function; List arguments; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java similarity index 76% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java index ad83cddf2f4..69487fd38b0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleNode.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java @@ -1,7 +1,6 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules; +package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; import java.util.Map; -import java.util.function.Function; public sealed interface RuleNode { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java similarity index 91% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java index 40661f3cf33..caee37c7a03 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTree.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java @@ -1,10 +1,9 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules; +package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; import org.apache.commons.lang3.ObjectUtils; import java.util.List; import java.util.Objects; -import java.util.function.Function; public class RuleTree { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java similarity index 89% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 9551bec2358..59110a41724 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleTreeFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -1,7 +1,7 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules; +package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import java.util.ArrayList; import java.util.List; @@ -10,7 +10,7 @@ public class RuleTreeFactory { - public static RuleTree> buildTree(Map> rules) { + public static RuleTree> buildTree(Map> rules) { return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 80af54b071c..17433cc7f79 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -1,15 +1,18 @@ package org.prebid.server.hooks.modules.rule.engine.v1; +import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; +import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleParser; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.InvocationResult; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; +import java.util.Map; import java.util.Objects; public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -26,8 +29,9 @@ public RuleEngineProcessedAuctionRequestHook(RequestRuleParser ruleParser) { public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { - final RequestRule rule = ruleParser.parse(invocationContext.accountConfig()); - final RequestRuleResult result = rule.process(auctionRequestPayload.bidRequest()); + final Map> rule = ruleParser.parse(invocationContext.accountConfig()); + final RuleResult result = rule.get(Stage.processed_auction_request) + .process(auctionRequestPayload.bidRequest()); return Future.succeededFuture(succeeded(payload -> AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder() From 95cab535bfbea840381060eb87f79da4e995923e Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 30 Apr 2025 18:27:32 +0300 Subject: [PATCH 12/94] WIP. --- .../{RulesCache.java => RuleRegistry.java} | 0 .../rule/engine/core/rules/RuleResult.java | 10 +++ .../rules/request/MatchingRequestRule.java | 79 ++++++++++--------- .../core/rules/request/RequestPayload.java | 12 +++ .../core/rules/request/RequestRuleParser.java | 26 +++--- .../core/rules/request/RequestSchema.java | 5 +- .../functions/IncludeBiddersFunction.java | 18 +++-- .../core/rules/result/ResultFunction.java | 7 +- .../rules/result/ResultFunctionResult.java | 13 --- .../rule/engine/core/rules/schema/Schema.java | 3 + .../rules/schema/SchemaFunctionArguments.java | 2 - .../rules/schema/SchemaFunctionHolder.java | 2 - .../org/prebid/server/model/UpdateResult.java | 2 +- 13 files changed, 97 insertions(+), 82 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/{RulesCache.java => RuleRegistry.java} (100%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RulesCache.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index 70fbafcadc6..14c128e7bbc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -1,8 +1,10 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.Value; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.v1.analytics.Tags; import org.prebid.server.model.UpdateResult; +import org.prebid.server.util.ListUtil; @Value(staticConstructor = "of") public class RuleResult { @@ -10,4 +12,12 @@ public class RuleResult { UpdateResult updateResult; Tags analyticsTags; + + public RuleResult mergeWith(RuleResult other) { + final boolean updated = other.updateResult.isUpdated() || updateResult.isUpdated(); + final T value = other.updateResult.getValue(); + final Tags tags = TagsImpl.of(ListUtil.union(other.analyticsTags.activities(), analyticsTags.activities())); + + return RuleResult.of(UpdateResult.of(updated, value), tags); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java index 87f042e44eb..e4254c208ff 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java @@ -2,75 +2,76 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import org.apache.commons.collections4.ListUtils; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; -import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; import org.prebid.server.model.UpdateResult; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; public class MatchingRequestRule implements Rule { - private final Schema schema; + private final Schema schema; private final RuleTree> ruleTree; - public MatchingRequestRule(Schema schema, RuleTree> ruleTree) { + public MatchingRequestRule(Schema schema, RuleTree> ruleTree) { this.schema = Objects.requireNonNull(schema); this.ruleTree = Objects.requireNonNull(ruleTree); } @Override public RuleResult process(BidRequest bidRequest) { - final List imps = ListUtils.emptyIfNull(bidRequest.getImp()); - final List> schemaFunctionHolders = schema.getFunctions(); - - final List activities = new ArrayList<>(); - BidRequest updatedBidRequest = bidRequest; - boolean updated = false; - - for (Imp imp : imps) { - final List matchers = schemaFunctionHolders.stream() - .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(bidRequest, holder.getArguments(), imp))) - .toList(); + RuleResult result = unalteredResult(bidRequest); + if (schema.getNames().contains("adUnitCode") || schema.getNames().contains("mediaType")) { + for (Imp imp : bidRequest.getImp()) { + final RuleResult updateResult = processRule( + result.getUpdateResult().getValue(), imp.getId()); + result = result.mergeWith(updateResult); + } + } else { + result = processRule(bidRequest, null); + } - final RuleConfig ruleConfig = ruleTree.getValue(matchers); + return result; + } - for (RuleAction action : ruleConfig.getActions()) { - final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( - null, - "analyticsKey", - ruleConfig.getRuleFired(), - "modelVersion"); + private RuleResult processRule(BidRequest bidRequest, String impId) { + final RequestPayload payload = RequestPayload.of(bidRequest, impId); + final List matchers = schema.getFunctions().stream() + .map(holder -> holder.getSchemaFunction() + .extract(SchemaFunctionArguments.of(payload, holder.getArguments()))) + .toList(); - final ResultFunction function = action.getFunction(); + final RuleConfig ruleConfig = ruleTree.getValue(matchers); + RuleResult result = unalteredResult(bidRequest); + for (RuleAction action : ruleConfig.getActions()) { + final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( + null, + "analyticsKey", + ruleConfig.getRuleFired(), + "modelVersion"); - for (ResultFunctionArguments arguments : action.getArguments()) { - final ResultFunctionResult bidRequestResult = function.apply( - arguments, infrastructureArguments, updatedBidRequest); - final UpdateResult updateResult = bidRequestResult.getUpdateResult(); + for (ResultFunctionArguments arguments : action.getArguments()) { + final RuleResult updateResult = action.getFunction() + .apply(arguments, infrastructureArguments, result.getUpdateResult().getValue()); - updated = updateResult.isUpdated() || updated; - updatedBidRequest = updateResult.getValue(); - activities.addAll(bidRequestResult.getAnalyticsTags().activities()); - } + result = result.mergeWith(updateResult); } } - return RuleResult.of(UpdateResult.of(updated, updatedBidRequest), TagsImpl.of(activities)); + return result; + } + + private static RuleResult unalteredResult(BidRequest bidRequest) { + return RuleResult.of(UpdateResult.unaltered(bidRequest), TagsImpl.of(Collections.emptyList())); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java new file mode 100644 index 00000000000..54ddd04006f --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java @@ -0,0 +1,12 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request; + +import com.iab.openrtb.request.BidRequest; +import lombok.Value; + +@Value(staticConstructor = "of") +public class RequestPayload { + + BidRequest bidRequest; + + String impId; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java index eab55f5f8b0..ce067ab6c42 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java @@ -9,22 +9,24 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ExcludeBiddersArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedEntry; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.util.random.RandomGenerator; import java.util.stream.Collectors; @@ -54,8 +56,7 @@ public Map> parse(ObjectNode accountConfig) { Collectors.toList()))) .entrySet() .stream() - .collect(Collectors.toMap( - e->e.getKey(), entry -> CompositeRule.of(entry.getValue()))); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> CompositeRule.of(entry.getValue()))); } private Rule toRule(List modelGroupConfigs) { @@ -67,7 +68,8 @@ private Rule toRule(List modelGroupConfigs) { } private static Rule toRule(ModelGroupConfig modelGroupConfig) { - final Schema schema = parseSchema(modelGroupConfig); + final Schema schema = parseSchema(modelGroupConfig.getSchema()); + final Map> rules = modelGroupConfig.getRules().stream() .map(RequestRuleParser::toRuleResult) .collect(Collectors.toMap(RuleConfig::getRuleFired, Function.identity())); @@ -76,15 +78,15 @@ private static Rule toRule(ModelGroupConfig modelGroupConfig) { return new MatchingRequestRule(schema, ruleTree); } - private static Schema parseSchema(ModelGroupConfig modelGroupConfig) { - final List> schemaFunctions = modelGroupConfig.getSchema().stream() + private static Schema parseSchema(List schema) { + final Set names = schema.stream().map(SchemaFunctionConfig::getFunction).collect(Collectors.toSet()); + + final List> schemaFunctions = schema.stream() .map(config -> SchemaFunctionHolder.of( - config.getFunction(), - RequestSchema.schemaFunctionByName(config.getFunction()), - config.getArgs())) + RequestSchema.schemaFunctionByName(config.getFunction()), config.getArgs())) .toList(); - return Schema.of(schemaFunctions); + return Schema.of(names, schemaFunctions); } private static RuleConfig toRuleResult(org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleConfig ruleConfig) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index 33fa6b52254..5b9759898ac 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -4,7 +4,6 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; -import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.CollectionUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; @@ -23,10 +22,10 @@ private RequestSchema() { public static final String AD_UNIT_CODE_FUNCTION = "adUnitCode"; public static final String MEDIA_TYPE_FUNCTION = "mediaType"; - private static final Map> SCHEMA_FUNCTIONS = Map.of( + private static final Map> SCHEMA_FUNCTIONS = Map.of( AD_UNIT_CODE_FUNCTION, RequestSchema::deviceCountryExtractor); - public static SchemaFunction schemaFunctionByName(String function) { + public static SchemaFunction schemaFunctionByName(String function) { return SCHEMA_FUNCTIONS.get(function); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java index b218fd9ddef..fa5c307995b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java @@ -1,24 +1,28 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions; -import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.BidRequest; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; import org.prebid.server.model.UpdateResult; import java.util.Collections; -public class IncludeBiddersFunction implements ResultFunction { +public class IncludeBiddersFunction implements ResultFunction { @Override - public ResultFunctionResult apply(ResultFunctionArguments arguments, - InfrastructureArguments infrastructureArguments, - Imp operand) { + public RuleResult apply(ResultFunctionArguments arguments, + InfrastructureArguments infrastructureArguments, + BidRequest operand) { + return RuleResult.of(UpdateResult.unaltered(operand), TagsImpl.of(Collections.emptyList())); + } - return ResultFunctionResult.of(UpdateResult.unaltered(operand), TagsImpl.of(Collections.emptyList())); + @Override + public void validateArguments(ResultFunctionArguments arguments) { + arguments } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index abe3ecf9913..3f28c0ad9c1 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -1,13 +1,14 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; public interface ResultFunction { - ResultFunctionResult apply(ResultFunctionArguments arguments, - InfrastructureArguments infrastructureArguments, - T operand); + RuleResult apply(ResultFunctionArguments arguments, + InfrastructureArguments infrastructureArguments, + T operand); void validateArguments(ResultFunctionArguments arguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java deleted file mode 100644 index db473536266..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionResult.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result; - -import lombok.Value; -import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; - -@Value(staticConstructor = "of") -public class ResultFunctionResult { - - UpdateResult updateResult; - - Tags analyticsTags; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java index 9bfa3804d9b..e57d0494688 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -3,9 +3,12 @@ import lombok.Value; import java.util.List; +import java.util.Set; @Value(staticConstructor = "of") public class Schema { + Set names; + List> functions; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java index 8d6a69d2bcd..0be4c801efa 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -12,6 +12,4 @@ public class SchemaFunctionArguments { T operand; List args; - - Imp imp; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java index f074eb1659c..f9f4fe4f32d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -8,8 +8,6 @@ @Value(staticConstructor = "of") public class SchemaFunctionHolder { - String name; - SchemaFunction schemaFunction; List arguments; diff --git a/src/main/java/org/prebid/server/model/UpdateResult.java b/src/main/java/org/prebid/server/model/UpdateResult.java index 3c28be53ebe..a96275d0e42 100644 --- a/src/main/java/org/prebid/server/model/UpdateResult.java +++ b/src/main/java/org/prebid/server/model/UpdateResult.java @@ -2,7 +2,7 @@ import lombok.Value; -@Value +@Value(staticConstructor = "of") public class UpdateResult { boolean updated; From 66bcf44c54381432cc260c29a65398167b44ab48 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 30 Apr 2025 18:37:27 +0300 Subject: [PATCH 13/94] WIP. --- ...RuleConfig.java => AccountRuleConfig.java} | 2 +- .../core/config/model/ModelGroupConfig.java | 2 +- .../rule/engine/core/rules/RuleConfig.java | 2 +- .../rules/request/MatchingRequestRule.java | 2 +- .../core/rules/request/RequestRuleParser.java | 22 +++++++++---------- .../engine/core/rules/result/RuleAction.java | 4 ++-- .../core/rules/tree/RuleTreeFactory.java | 21 ++++++------------ 7 files changed, 23 insertions(+), 32 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/{RuleConfig.java => AccountRuleConfig.java} (87%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java similarity index 87% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java index d1b513c8c67..da77ada6e71 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java @@ -5,7 +5,7 @@ import java.util.List; @Value(staticConstructor = "of") -public class RuleConfig { +public class AccountRuleConfig { List conditions; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index 1435451c2d2..a64f2e86c10 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -20,5 +20,5 @@ public class ModelGroupConfig { @JsonProperty("default") RuleFunctionConfig defaultAction; - List rules; + List rules; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java index e7346d9eda1..ab8b28c9aec 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java @@ -8,7 +8,7 @@ @Value(staticConstructor = "of") public class RuleConfig { - String ruleFired; + String condition; List> actions; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java index e4254c208ff..41d184d2e33 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java @@ -57,7 +57,7 @@ private RuleResult processRule(BidRequest bidRequest, String impId) final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( null, "analyticsKey", - ruleConfig.getRuleFired(), + ruleConfig.getCondition(), "modelVersion"); for (ResultFunctionArguments arguments : action.getArguments()) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java index ce067ab6c42..836e535dbab 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java @@ -7,6 +7,7 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountRuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; @@ -15,7 +16,6 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ExcludeBiddersArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; @@ -64,15 +64,15 @@ private Rule toRule(List modelGroupConfigs) { .map(config -> WeightedEntry.of(config.getWeight(), toRule(config))) .toList(); - return new WeightedRule(randomGenerator, new WeightedList<>(weightedRules)); + return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } - private static Rule toRule(ModelGroupConfig modelGroupConfig) { - final Schema schema = parseSchema(modelGroupConfig.getSchema()); + private static Rule toRule(ModelGroupConfig config) { + final Schema schema = parseSchema(config.getSchema()); - final Map> rules = modelGroupConfig.getRules().stream() - .map(RequestRuleParser::toRuleResult) - .collect(Collectors.toMap(RuleConfig::getRuleFired, Function.identity())); + final List> rules = config.getRules().stream() + .map(RequestRuleParser::toRuleConfig) + .toList(); final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); return new MatchingRequestRule(schema, ruleTree); @@ -89,13 +89,11 @@ private static Schema parseSchema(List sch return Schema.of(names, schemaFunctions); } - private static RuleConfig toRuleResult(org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleConfig ruleConfig) { + private static RuleConfig toRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); final List> actions = ruleConfig.getResults().stream() - .map(result -> RuleAction.of( - // TODO: parse result arguments properly - RequestSchema.resultFunctionByName(result.getFunction()), - List.of(ExcludeBiddersArguments.of(List.of(), 203, false, "test")))) + .map(config -> RuleAction.of( + RequestSchema.resultFunctionByName(config.getFunction()), config.getArgs())) .toList(); return RuleConfig.of(ruleFired, actions); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java index 740a240e3b6..1aa32b0668b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; import java.util.List; @@ -10,5 +10,5 @@ public class RuleAction { ResultFunction function; - List arguments; + List arguments; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 59110a41724..8bd951e2056 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -3,29 +3,22 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class RuleTreeFactory { - public static RuleTree> buildTree(Map> rules) { + public static RuleTree> buildTree(List> rules) { return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); } - private static List> toParsingContexts(Map rules) { - final List> contexts = new ArrayList<>(); - - for (Map.Entry entry : rules.entrySet()) { - final List arguments = List.of(StringUtils.defaultString(entry.getKey()).split("\\|")); - final T value = entry.getValue(); - - - contexts.add(new ParsingContext<>(arguments, value)); - } - - return contexts; + private static List>> toParsingContexts(List> rules) { + return rules.stream() + .map(rule -> new ParsingContext<>( + List.of(StringUtils.defaultString(rule.getCondition()).split("\\|")), + rule)) + .toList(); } private static RuleNode parseRuleNode(List> parsingContexts) { From 45782a553160d5ffecff0037f43bca28b6adbbbd Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 30 Apr 2025 18:44:29 +0300 Subject: [PATCH 14/94] WIP. --- .../core/rules/request/MatchingRequestRule.java | 2 +- .../core/rules/request/RequestSchema.java | 2 +- .../functions/IncludeBiddersFunction.java | 2 +- .../core/rules/result/ResultFunction.java | 11 +++++------ .../rules/result/ResultFunctionArguments.java | 17 +++++++++++++++++ .../arguments/ExcludeBiddersArguments.java | 17 ----------------- .../arguments/IncludeBiddersArguments.java | 17 ----------------- .../result/arguments/LogATagArguments.java | 9 --------- .../arguments/ResultFunctionArguments.java | 5 ----- .../core/rules/schema/SchemaFunction.java | 6 +++++- .../rules/schema/SchemaFunctionArguments.java | 3 +-- 11 files changed, 31 insertions(+), 60 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java index 41d184d2e33..df92f9aec27 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java @@ -8,7 +8,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index 5b9759898ac..fa5d98ec1fc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -35,7 +35,7 @@ public static ResultFunction resultFunctionByName(String function) { public static String deviceCountryExtractor(SchemaFunctionArguments arguments) { final BidRequest bidRequest = arguments.getOperand(); - final List args = arguments.getArgs(); + final List args = arguments.getConfigArguments(); if (CollectionUtils.isEmpty(args)) { return Optional.ofNullable(bidRequest.getDevice()) .map(Device::getGeo) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java index fa5c307995b..d6e0b9ec8f2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java @@ -5,7 +5,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.model.UpdateResult; import java.util.Collections; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index 3f28c0ad9c1..7d56dc3b020 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -1,14 +1,13 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; +import com.fasterxml.jackson.databind.JsonNode; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.ResultFunctionArguments; + +import java.util.List; public interface ResultFunction { - RuleResult apply(ResultFunctionArguments arguments, - InfrastructureArguments infrastructureArguments, - T operand); + RuleResult apply(ResultFunctionArguments arguments); - void validateArguments(ResultFunctionArguments arguments); + void validateArguments(List configArguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java new file mode 100644 index 00000000000..59b270d6c2e --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java @@ -0,0 +1,17 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; + +import java.util.List; + +@Value(staticConstructor = "of") +public class ResultFunctionArguments { + + T operand; + + List configArguments; + + InfrastructureArguments infrastructureArguments; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java deleted file mode 100644 index 2dc1c6623d7..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ExcludeBiddersArguments.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; - -import lombok.Value; - -import java.util.List; - -@Value(staticConstructor = "of") -public class ExcludeBiddersArguments implements ResultFunctionArguments { - - List bidders; - - Integer seatNonBid; - - boolean ifSyncedId; - - String analyticsValue; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java deleted file mode 100644 index eb301047c24..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/IncludeBiddersArguments.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; - -import lombok.Value; - -import java.util.List; - -@Value(staticConstructor = "of") -public class IncludeBiddersArguments implements ResultFunctionArguments { - - List bidders; - - Integer seatNonBid; - - boolean ifSyncedId; - - String analyticsValue; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java deleted file mode 100644 index 97eb4127bc7..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/LogATagArguments.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; - -import lombok.Value; - -@Value(staticConstructor = "of") -public class LogATagArguments implements ResultFunctionArguments { - - String analyticsValue; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java deleted file mode 100644 index d3050ccdf2c..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/ResultFunctionArguments.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; - -public sealed interface ResultFunctionArguments permits - ExcludeBiddersArguments, IncludeBiddersArguments, LogATagArguments { -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index dd3131abab2..9c317cbac9d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -1,8 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.List; + public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); - void validate(SchemaFunctionArguments arguments); + void validate(List configArgs); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java index 0be4c801efa..7095031d28c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -1,7 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; import com.fasterxml.jackson.databind.JsonNode; -import com.iab.openrtb.request.Imp; import lombok.Value; import java.util.List; @@ -11,5 +10,5 @@ public class SchemaFunctionArguments { T operand; - List args; + List configArguments; } From 9f2af720fefd31d863aaf8347dfa56123096e5f8 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 30 Apr 2025 19:28:31 +0300 Subject: [PATCH 15/94] WIP. --- .../InvalidResultFunctionException.java | 8 +++ .../InvalidSchemaFunctionException.java | 8 +++ ...uestRule.java => RequestMatchingRule.java} | 18 +++---- .../core/rules/request/RequestRuleParser.java | 3 +- .../core/rules/request/RequestSchema.java | 49 ++++++++----------- .../functions/IncludeBiddersFunction.java | 21 +++++--- .../functions/DeviceCountryFunction.java | 42 ++++++++++++++++ .../InfrastructureArguments.java | 2 +- .../core/rules/result/ResultFunction.java | 2 +- .../rules/result/ResultFunctionArguments.java | 1 - .../engine/core/rules/result/RuleAction.java | 2 +- .../core/rules/schema/SchemaFunction.java | 2 +- 12 files changed, 104 insertions(+), 54 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/{MatchingRequestRule.java => RequestMatchingRule.java} (85%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/{arguments => }/InfrastructureArguments.java (94%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java new file mode 100644 index 00000000000..356f7ef53b4 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.exception; + +public class InvalidResultFunctionException extends RuntimeException { + + public InvalidResultFunctionException(String function) { + super("Invalid result function: " + function); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java new file mode 100644 index 00000000000..f1b0f05f7b6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.exception; + +public class InvalidSchemaFunctionException extends RuntimeException { + + public InvalidSchemaFunctionException(String function) { + super("Invalid schema function: " + function); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java similarity index 85% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index df92f9aec27..cdfb4dd489d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/MatchingRequestRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -6,9 +6,9 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; @@ -18,12 +18,12 @@ import java.util.List; import java.util.Objects; -public class MatchingRequestRule implements Rule { +public class RequestMatchingRule implements Rule { private final Schema schema; private final RuleTree> ruleTree; - public MatchingRequestRule(Schema schema, RuleTree> ruleTree) { + public RequestMatchingRule(Schema schema, RuleTree> ruleTree) { this.schema = Objects.requireNonNull(schema); this.ruleTree = Objects.requireNonNull(ruleTree); } @@ -53,19 +53,17 @@ private RuleResult processRule(BidRequest bidRequest, String impId) final RuleConfig ruleConfig = ruleTree.getValue(matchers); RuleResult result = unalteredResult(bidRequest); + for (RuleAction action : ruleConfig.getActions()) { final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( null, "analyticsKey", ruleConfig.getCondition(), "modelVersion"); + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + result.getUpdateResult().getValue(), action.getConfigArguments(), infrastructureArguments); - for (ResultFunctionArguments arguments : action.getArguments()) { - final RuleResult updateResult = action.getFunction() - .apply(arguments, infrastructureArguments, result.getUpdateResult().getValue()); - - result = result.mergeWith(updateResult); - } + result = result.mergeWith(action.getFunction().apply(arguments)); } return result; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java index 836e535dbab..634c8617fa3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java @@ -27,7 +27,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Function; import java.util.random.RandomGenerator; import java.util.stream.Collectors; @@ -75,7 +74,7 @@ private static Rule toRule(ModelGroupConfig config) { .toList(); final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); - return new MatchingRequestRule(schema, ruleTree); + return new RequestMatchingRule(schema, ruleTree); } private static Schema parseSchema(List schema) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index fa5d98ec1fc..7fc8ce5d519 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -1,48 +1,39 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request; -import com.fasterxml.jackson.databind.JsonNode; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Geo; -import org.apache.commons.collections4.CollectionUtils; +import lombok.experimental.UtilityClass; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions.IncludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions.DeviceCountryFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import java.util.List; import java.util.Map; -import java.util.Optional; +@UtilityClass public class RequestSchema { - private RequestSchema() { - } - - public static final String DEVICE_COUNTRY_FUNCTION = "deviceCountry"; - public static final String AD_UNIT_CODE_FUNCTION = "adUnitCode"; - public static final String MEDIA_TYPE_FUNCTION = "mediaType"; - private static final Map> SCHEMA_FUNCTIONS = Map.of( - AD_UNIT_CODE_FUNCTION, RequestSchema::deviceCountryExtractor); + DeviceCountryFunction.NAME, DeviceCountryFunction.INSTANCE); - public static SchemaFunction schemaFunctionByName(String function) { - return SCHEMA_FUNCTIONS.get(function); - } + private static final Map> RESULT_FUNCTIONS = Map.of( + IncludeBiddersFunction.NAME, IncludeBiddersFunction.INSTANCE); + + public static SchemaFunction schemaFunctionByName(String name) { + final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); + if (function == null) { + throw new InvalidSchemaFunctionException(name); + } - public static ResultFunction resultFunctionByName(String function) { - return null; + return function; } - public static String deviceCountryExtractor(SchemaFunctionArguments arguments) { - final BidRequest bidRequest = arguments.getOperand(); - final List args = arguments.getConfigArguments(); - if (CollectionUtils.isEmpty(args)) { - return Optional.ofNullable(bidRequest.getDevice()) - .map(Device::getGeo) - .map(Geo::getCountry) - .orElse(null); + public static ResultFunction resultFunctionByName(String name) { + final ResultFunction function = RESULT_FUNCTIONS.get(name); + if (function == null) { + throw new InvalidSchemaFunctionException(name); } - return "true"; + return function; } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java index d6e0b9ec8f2..9de13f717bc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java @@ -1,28 +1,33 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions; +import com.fasterxml.jackson.databind.JsonNode; import com.iab.openrtb.request.BidRequest; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.model.UpdateResult; import java.util.Collections; +import java.util.List; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class IncludeBiddersFunction implements ResultFunction { + public static final IncludeBiddersFunction INSTANCE = new IncludeBiddersFunction(); - @Override - public RuleResult apply(ResultFunctionArguments arguments, - InfrastructureArguments infrastructureArguments, - BidRequest operand) { + public static final String NAME = "includeBidders"; - return RuleResult.of(UpdateResult.unaltered(operand), TagsImpl.of(Collections.emptyList())); + @Override + public RuleResult apply(ResultFunctionArguments arguments) { + return RuleResult.of( + UpdateResult.unaltered(arguments.getOperand()), + TagsImpl.of(Collections.emptyList())); } @Override - public void validateArguments(ResultFunctionArguments arguments) { - arguments + public void validateConfigArguments(List configArguments) { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java new file mode 100644 index 00000000000..e54f94e9d9a --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java @@ -0,0 +1,42 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +import java.util.List; +import java.util.Optional; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DeviceCountryFunction implements SchemaFunction { + + public static final String NAME = "deviceCountry"; + + public static final DeviceCountryFunction INSTANCE = new DeviceCountryFunction(); + + @Override + public String extract(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + final List args = arguments.getConfigArguments(); + if (CollectionUtils.isEmpty(args)) { + return Optional.ofNullable(bidRequest.getDevice()) + .map(Device::getGeo) + .map(Geo::getCountry) + .orElse(null); + } + + return "true"; + } + + @Override + public void validate(List configArguments) { + + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java similarity index 94% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 8dfabd5ac53..49ef2fbc4d8 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/arguments/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -1,4 +1,4 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments; +package org.prebid.server.hooks.modules.rule.engine.core.rules.result; import lombok.Value; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index 7d56dc3b020..bc168dbe45b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -9,5 +9,5 @@ public interface ResultFunction { RuleResult apply(ResultFunctionArguments arguments); - void validateArguments(List configArguments); + void validateConfigArguments(List configArguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java index 59b270d6c2e..d8d58e5a7cb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.arguments.InfrastructureArguments; import java.util.List; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java index 1aa32b0668b..09e20fc6d57 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java @@ -10,5 +10,5 @@ public class RuleAction { ResultFunction function; - List arguments; + List configArguments; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 9c317cbac9d..781adb3dc7a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -8,5 +8,5 @@ public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); - void validate(List configArgs); + void validate(List configArguments); } From 8f0aed7f67d711496c57f6906a476ffab3b153d9 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 00:37:35 +0300 Subject: [PATCH 16/94] WIP. --- .../rules/request/RequestMatchingRule.java | 23 +++++++++---------- .../core/rules/request/RequestSchema.java | 3 +++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index cdfb4dd489d..ae0311dafe7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.SetUtils; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; @@ -30,18 +30,17 @@ public RequestMatchingRule(Schema schema, RuleTree process(BidRequest bidRequest) { - RuleResult result = unalteredResult(bidRequest); - if (schema.getNames().contains("adUnitCode") || schema.getNames().contains("mediaType")) { - for (Imp imp : bidRequest.getImp()) { - final RuleResult updateResult = processRule( - result.getUpdateResult().getValue(), imp.getId()); - result = result.mergeWith(updateResult); - } - } else { - result = processRule(bidRequest, null); - } + return SetUtils.intersection(schema.getNames(), RequestSchema.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() + ? processRule(bidRequest, null) + : processPerImpRule(bidRequest); + } - return result; + private RuleResult processPerImpRule(BidRequest bidRequest) { + return bidRequest.getImp().stream().reduce( + unalteredResult(bidRequest), + (result, imp) -> + result.mergeWith(processRule(result.getUpdateResult().getValue(), imp.getId())), + RuleResult::mergeWith); } private RuleResult processRule(BidRequest bidRequest, String impId) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java index 7fc8ce5d519..a8e993b1cf3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java @@ -9,10 +9,13 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import java.util.Map; +import java.util.Set; @UtilityClass public class RequestSchema { + public static Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(); + private static final Map> SCHEMA_FUNCTIONS = Map.of( DeviceCountryFunction.NAME, DeviceCountryFunction.INSTANCE); From 5ef2d232feaf0705fc49711a3a997a8507ddc6bd Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 02:24:51 +0300 Subject: [PATCH 17/94] WIP. --- .../config/RuleEngineModuleConfiguration.java | 43 +++++++++- .../rule/engine/core/cache/RuleRegistry.java | 39 ++++++++-- .../core/config/AccountConfigParser.java | 38 +++++++++ .../StageConfigParser.java} | 78 +++++++++---------- .../rule/engine/core/request/ModelGroup.java | 9 --- .../core/request/RequestModelGroup.java | 8 -- .../rule/engine/core/request/RequestRule.java | 15 ---- .../core/request/RequestRuleContext.java | 15 ---- .../rule/engine/core/request/RuleSet.java | 9 --- .../core/rules/MatchingRuleFactory.java | 9 +++ .../rule/engine/core/rules/PerStageRule.java | 15 ++++ .../engine/core/rules/StageSpecification.java | 11 +++ .../rules/request/RequestMatchingRule.java | 2 +- ...tSchema.java => RequestSpecification.java} | 16 ++-- .../rule/engine/v1/RuleEngineModule.java | 6 +- ...RuleEngineProcessedAuctionRequestHook.java | 55 +++++++++---- 16 files changed, 240 insertions(+), 128 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{rules/request/RequestRuleParser.java => config/StageConfigParser.java} (50%) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/{RequestSchema.java => RequestSpecification.java} (70%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 80c4105b944..3e9b0b90244 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -1,16 +1,55 @@ package org.prebid.server.hooks.modules.rule.engine.config; +import com.iab.openrtb.request.BidRequest; +import io.vertx.core.Vertx; +import org.prebid.server.hooks.execution.model.Stage; +import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; +import org.prebid.server.json.ObjectMapperProvider; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.concurrent.ThreadLocalRandom; + @Configuration @ConditionalOnProperty(prefix = "hooks." + RuleEngineModule.CODE, name = "enabled", havingValue = "true") public class RuleEngineModuleConfiguration { @Bean - RuleEngineModule ruleEngineModule() { - return new RuleEngineModule(); + RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { + return new RuleEngineModule(ruleRegistry); + } + + @Bean + StageConfigParser processedAuctionRequestStageParser() { + return new StageConfigParser<>( + () -> ThreadLocalRandom.current().nextLong(), + Stage.processed_auction_request, + RequestSpecification.INSTANCE, + RequestMatchingRule::new); + } + + @Bean + AccountConfigParser accountConfigParser( + StageConfigParser processedAuctionRequestStageParser) { + + return new AccountConfigParser(ObjectMapperProvider.mapper(), processedAuctionRequestStageParser); + } + + @Bean + RuleRegistry ruleRegistry( + @Value("{hooks.modules.rule-engine.rule-cache-expire-after-minutes}") long cacheExpireAfterMinutes, + @Value("{hooks.modules.rule-engine.rule-cache-max-size}") long cacheMaxSize, + AccountConfigParser accountConfigParser, + Vertx vertx) { + + return new RuleRegistry(cacheExpireAfterMinutes, cacheMaxSize, accountConfigParser, vertx); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java index f44cd77b975..cf6f9713b5c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java @@ -1,16 +1,41 @@ package org.prebid.server.hooks.modules.rule.engine.core.cache; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.benmanes.caffeine.cache.Caffeine; -import com.iab.openrtb.request.Imp; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import java.util.Objects; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; -public class RulesCache { +public class RuleRegistry { - final ConcurrentMap> cache = Caffeine.newBuilder() - .expireAfterAccess(100, TimeUnit.HOURS) - .maximumSize(10000) - .>build() - .asMap(); + private final AccountConfigParser parser; + private final Vertx vertx; + + + private final ConcurrentMap> accountIdToRules; + + public RuleRegistry(long cacheExpireAfterMinutes, + long cacheMaxSize, + AccountConfigParser parser, + Vertx vertx) { + + this.parser = Objects.requireNonNull(parser); + this.vertx = Objects.requireNonNull(vertx); + + this.accountIdToRules = Caffeine.newBuilder() + .expireAfterAccess(cacheExpireAfterMinutes, TimeUnit.MINUTES) + .maximumSize(cacheMaxSize) + .>build() + .asMap(); + } + + public Future forAccount(String accountId, ObjectNode config) { + return accountIdToRules.computeIfAbsent( + accountId, (ignored) -> vertx.executeBlocking(() -> parser.parse(config))); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java new file mode 100644 index 00000000000..06ceaa40680 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -0,0 +1,38 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; + +import java.util.Objects; + +public class AccountConfigParser { + + private final ObjectMapper mapper; + private final StageConfigParser processedAuctionRequestStageParser; + + public AccountConfigParser(ObjectMapper mapper, + StageConfigParser processedAuctionRequestStageParser) { + + this.mapper = Objects.requireNonNull(mapper); + this.processedAuctionRequestStageParser = Objects.requireNonNull(processedAuctionRequestStageParser); + } + + public PerStageRule parse(ObjectNode accountConfig) { + final AccountConfig parsedConfig; + try { + parsedConfig = mapper.treeToValue(accountConfig, AccountConfig.class); + } catch (JsonProcessingException e) { + throw new PreBidException(e.getMessage()); + } + + return PerStageRule.builder() + .processedAuctionRequestRule(processedAuctionRequestStageParser.parse(parsedConfig)) + .build(); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java similarity index 50% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 634c8617fa3..3019ff91e51 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestRuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -1,10 +1,5 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; +package org.prebid.server.hooks.modules.rule.engine.core.config; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.iab.openrtb.request.BidRequest; -import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountRuleConfig; @@ -12,8 +7,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; @@ -24,75 +21,74 @@ import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.random.RandomGenerator; import java.util.stream.Collectors; -public class RequestRuleParser { +public class StageConfigParser { - private final ObjectMapper mapper; private final RandomGenerator randomGenerator; + private final StageSpecification specification; + private final Stage stage; + private final MatchingRuleFactory matchingRuleFactory; + + public StageConfigParser(RandomGenerator randomGenerator, + Stage stage, + StageSpecification specification, + MatchingRuleFactory matchingRuleFactory) { - public RequestRuleParser(ObjectMapper mapper, RandomGenerator randomGenerator) { - this.mapper = Objects.requireNonNull(mapper); this.randomGenerator = Objects.requireNonNull(randomGenerator); + this.stage = Objects.requireNonNull(stage); + this.specification = Objects.requireNonNull(specification); + this.matchingRuleFactory = Objects.requireNonNull(matchingRuleFactory); } - public Map> parse(ObjectNode accountConfig) { - final AccountConfig parsedConfig; - try { - parsedConfig = mapper.treeToValue(accountConfig, AccountConfig.class); - } catch (JsonProcessingException e) { - throw new PreBidException(e.getMessage()); - } - - return parsedConfig.getRuleSets().stream() - .collect(Collectors.groupingBy(RuleSetConfig::getStage, - Collectors.mapping( - ruleSetConfig -> toRule(ruleSetConfig.getModelGroups()), - Collectors.toList()))) - .entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> CompositeRule.of(entry.getValue()))); + public Rule parse(AccountConfig config) { + final List> stageSubrules = config.getRuleSets().stream() + .filter(ruleSet -> stage.equals(ruleSet.getStage())) + .map(RuleSetConfig::getModelGroups) + .map(this::toRule) + .toList(); + + return CompositeRule.of(stageSubrules); } - private Rule toRule(List modelGroupConfigs) { - final List>> weightedRules = modelGroupConfigs.stream() + private Rule toRule(List modelGroupConfigs) { + final List>> weightedRules = modelGroupConfigs.stream() .map(config -> WeightedEntry.of(config.getWeight(), toRule(config))) .toList(); return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } - private static Rule toRule(ModelGroupConfig config) { - final Schema schema = parseSchema(config.getSchema()); + private Rule toRule(ModelGroupConfig config) { + final Schema schema = parseSchema(config.getSchema()); - final List> rules = config.getRules().stream() - .map(RequestRuleParser::toRuleConfig) + final List> rules = config.getRules().stream() + .map(this::toRuleConfig) .toList(); - final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); + final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); - return new RequestMatchingRule(schema, ruleTree); + return matchingRuleFactory.create(schema, ruleTree); } - private static Schema parseSchema(List schema) { + private Schema parseSchema(List schema) { final Set names = schema.stream().map(SchemaFunctionConfig::getFunction).collect(Collectors.toSet()); - final List> schemaFunctions = schema.stream() + final List> schemaFunctions = schema.stream() .map(config -> SchemaFunctionHolder.of( - RequestSchema.schemaFunctionByName(config.getFunction()), config.getArgs())) + specification.schemaFunctionByName(config.getFunction()), config.getArgs())) .toList(); return Schema.of(names, schemaFunctions); } - private static RuleConfig toRuleConfig(AccountRuleConfig ruleConfig) { + private RuleConfig toRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); - final List> actions = ruleConfig.getResults().stream() + final List> actions = ruleConfig.getResults().stream() .map(config -> RuleAction.of( - RequestSchema.resultFunctionByName(config.getFunction()), config.getArgs())) + specification.resultFunctionByName(config.getFunction()), config.getArgs())) .toList(); return RuleConfig.of(ruleFired, actions); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java deleted file mode 100644 index c3a47d515ab..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/ModelGroup.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -import lombok.Value; - -@Value(staticConstructor = "of") -public class ModelGroup { - - -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java deleted file mode 100644 index 2033a2b2477..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestModelGroup.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -public class RequestModelGroup { - - String model; - - -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java deleted file mode 100644 index 2d731b0165e..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRule.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -import lombok.Value; - -import java.util.List; - -@Value(staticConstructor = "of") -public class RequestRule { - - String name; - - String version; - - List ruleSets; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java deleted file mode 100644 index 6f99ee34574..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; -import lombok.Value; - -@Value(staticConstructor = "of") -public class RequestRuleContext { - - boolean validation; - - Imp imp; - - BidRequest bidRequest; -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java deleted file mode 100644 index 9c0a27cd453..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RuleSet.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; - -import lombok.Value; - -@Value(staticConstructor = "of") -public class RuleSet { - - -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java new file mode 100644 index 00000000000..1dd22261709 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java @@ -0,0 +1,9 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; + +public interface MatchingRuleFactory { + + Rule create(Schema schema, RuleTree> ruleTree); +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java new file mode 100644 index 00000000000..b4df69e44e2 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -0,0 +1,15 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import com.iab.openrtb.request.BidRequest; +import lombok.Builder; +import lombok.Value; +import lombok.experimental.Accessors; + +@Builder +@Accessors(fluent = true) +@Value(staticConstructor = "of") +public class PerStageRule { + + Rule processedAuctionRequestRule; +} + diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java new file mode 100644 index 00000000000..fb4681b1dd5 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java @@ -0,0 +1,11 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; + +public interface StageSpecification { + + SchemaFunction schemaFunctionByName(String name); + + ResultFunction resultFunctionByName(String name); +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index ae0311dafe7..29c1e41dd86 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -30,7 +30,7 @@ public RequestMatchingRule(Schema schema, RuleTree process(BidRequest bidRequest) { - return SetUtils.intersection(schema.getNames(), RequestSchema.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() + return SetUtils.intersection(schema.getNames(), RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() ? processRule(bidRequest, null) : processPerImpRule(bidRequest); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java similarity index 70% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java index a8e993b1cf3..95771367fd3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSchema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java @@ -1,7 +1,9 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.request; import com.iab.openrtb.request.BidRequest; -import lombok.experimental.UtilityClass; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions.DeviceCountryFunction; @@ -11,10 +13,12 @@ import java.util.Map; import java.util.Set; -@UtilityClass -public class RequestSchema { +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RequestSpecification implements StageSpecification { - public static Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(); + public static final RequestSpecification INSTANCE = new RequestSpecification(); + + public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(); private static final Map> SCHEMA_FUNCTIONS = Map.of( DeviceCountryFunction.NAME, DeviceCountryFunction.INSTANCE); @@ -22,7 +26,7 @@ public class RequestSchema { private static final Map> RESULT_FUNCTIONS = Map.of( IncludeBiddersFunction.NAME, IncludeBiddersFunction.INSTANCE); - public static SchemaFunction schemaFunctionByName(String name) { + public SchemaFunction schemaFunctionByName(String name) { final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); @@ -31,7 +35,7 @@ public static SchemaFunction schemaFunctionByName(String name) { return function; } - public static ResultFunction resultFunctionByName(String name) { + public ResultFunction resultFunctionByName(String name) { final ResultFunction function = RESULT_FUNCTIONS.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java index 931afb77530..fe41fc1ee28 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java @@ -1,5 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.v1; +import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.v1.Hook; import org.prebid.server.hooks.v1.InvocationContext; import org.prebid.server.hooks.v1.Module; @@ -13,9 +15,9 @@ public class RuleEngineModule implements Module { private final Collection> hooks; - public RuleEngineModule() { + public RuleEngineModule(RuleRegistry ruleRegistry) { this.hooks = Collections.singleton( - new RuleEngineProcessedAuctionRequestHook()); + new RuleEngineProcessedAuctionRequestHook(ruleRegistry)); } @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 17433cc7f79..35367e2b13f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -2,41 +2,70 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; -import org.prebid.server.hooks.execution.model.Stage; +import org.prebid.server.hooks.execution.v1.InvocationResultImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestRuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; +import org.prebid.server.hooks.v1.InvocationStatus; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; +import org.prebid.server.model.UpdateResult; -import java.util.Map; +import java.util.Collections; import java.util.Objects; public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { private static final String CODE = "rule-engine-processed-auction-request"; - private final RequestRuleParser ruleParser; + private final RuleRegistry ruleRegistry; - public RuleEngineProcessedAuctionRequestHook(RequestRuleParser ruleParser) { - this.ruleParser = Objects.requireNonNull(ruleParser); + public RuleEngineProcessedAuctionRequestHook(RuleRegistry ruleRegistry) { + this.ruleRegistry = Objects.requireNonNull(ruleRegistry); } @Override public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { - final Map> rule = ruleParser.parse(invocationContext.accountConfig()); - final RuleResult result = rule.get(Stage.processed_auction_request) - .process(auctionRequestPayload.bidRequest()); + final String accountId = invocationContext.auctionContext().getAccount().getId(); + return ruleRegistry.forAccount(accountId, invocationContext.accountConfig()) + .map(PerStageRule::processedAuctionRequestRule) + .map(rule -> executeSafely(rule, auctionRequestPayload.bidRequest())) + .map(RuleEngineProcessedAuctionRequestHook::succeeded) + .recover(RuleEngineProcessedAuctionRequestHook::failure); + } + + private static RuleResult executeSafely(Rule rule, BidRequest bidRequest) { + return rule != null + ? rule.process(bidRequest) + : RuleResult.of(UpdateResult.unaltered(bidRequest), TagsImpl.of(Collections.emptyList())); + } + + private static InvocationResult succeeded(RuleResult result) { + final UpdateResult updateResult = result.getUpdateResult(); + + return InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(updateResult.isUpdated() ? InvocationAction.update : InvocationAction.no_action) + .payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(updateResult.getValue())) + .analyticsTags(result.getAnalyticsTags()) + .build(); + } - return Future.succeededFuture(succeeded(payload -> - AuctionRequestPayloadImpl.of(payload.bidRequest().toBuilder() - .ext(originalBidRequest.getExt()) - .build()))); + private static Future> failure(Throwable error) { + return Future.succeededFuture( + InvocationResultImpl.builder() + .status(InvocationStatus.failure) + .action(InvocationAction.no_invocation) + .message(error.getMessage()) + .build()); } @Override From e5c5a8c5c40f593ab7807db971e423e246905aaa Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 03:14:07 +0300 Subject: [PATCH 18/94] Added removal of invalid configuration cache entries. --- .../config/RuleEngineModuleConfiguration.java | 4 ++-- .../rule/engine/core/cache/RuleRegistry.java | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 3e9b0b90244..29c41dcf996 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -45,8 +45,8 @@ AccountConfigParser accountConfigParser( @Bean RuleRegistry ruleRegistry( - @Value("{hooks.modules.rule-engine.rule-cache-expire-after-minutes}") long cacheExpireAfterMinutes, - @Value("{hooks.modules.rule-engine.rule-cache-max-size}") long cacheMaxSize, + @Value("${hooks.rule-engine.rule-cache-expire-after-minutes}") long cacheExpireAfterMinutes, + @Value("${hooks.rule-engine.rule-cache-max-size}") long cacheMaxSize, AccountConfigParser accountConfigParser, Vertx vertx) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java index cf6f9713b5c..8e4c785b0bc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java @@ -35,7 +35,19 @@ public RuleRegistry(long cacheExpireAfterMinutes, } public Future forAccount(String accountId, ObjectNode config) { + // TODO: think about adding exponential backoff for account with invalid config, + // since parsing is heavy operation + + // TODO: utilize timestamp for cache invalidation return accountIdToRules.computeIfAbsent( - accountId, (ignored) -> vertx.executeBlocking(() -> parser.parse(config))); + accountId, (ignored) -> vertx.executeBlocking(() -> parser.parse(config))) + .recover(error -> evictCacheAndRethrowError(accountId, error)); + } + + private Future evictCacheAndRethrowError(String accountId, Throwable error) { + accountIdToRules.compute( + accountId, (ignored, ruleFuture) -> ruleFuture == null || ruleFuture.failed() ? null : ruleFuture); + + return Future.failedFuture(error); } } From c7d421beac00ddffb93a6b0a18a0caf256c7ee21 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 04:55:40 +0300 Subject: [PATCH 19/94] Added default actions. --- .../engine/core/config/StageConfigParser.java | 41 ++++++---- .../core/config/model/ModelGroupConfig.java | 2 +- .../core/rules/AlternativeActionRule.java | 32 ++++++++ .../engine/core/rules/DefaultActionRule.java | 40 ++++++++++ .../core/rules/MatchingRuleFactory.java | 5 +- .../rule/engine/core/rules/NoOpRule.java | 16 ++++ .../rule/engine/core/rules/RuleResult.java | 6 ++ .../exception/NoMatchingRuleException.java | 8 ++ .../rules/request/RequestMatchingRule.java | 77 +++++++++++++------ .../rule/engine/core/rules/schema/Schema.java | 2 - .../rules/schema/SchemaFunctionHolder.java | 2 + .../rule/engine/core/rules/tree/RuleTree.java | 11 ++- .../rule/engine/core/util/WeightedEntry.java | 2 +- 13 files changed, 193 insertions(+), 51 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 3019ff91e51..d3aa1962b3d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -4,9 +4,12 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountRuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; @@ -22,9 +25,7 @@ import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.random.RandomGenerator; -import java.util.stream.Collectors; public class StageConfigParser { @@ -48,49 +49,59 @@ public Rule parse(AccountConfig config) { final List> stageSubrules = config.getRuleSets().stream() .filter(ruleSet -> stage.equals(ruleSet.getStage())) .map(RuleSetConfig::getModelGroups) - .map(this::toRule) + .map(this::parseModelGroupConfigs) .toList(); return CompositeRule.of(stageSubrules); } - private Rule toRule(List modelGroupConfigs) { + private Rule parseModelGroupConfigs(List modelGroupConfigs) { final List>> weightedRules = modelGroupConfigs.stream() - .map(config -> WeightedEntry.of(config.getWeight(), toRule(config))) + .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } - private Rule toRule(ModelGroupConfig config) { + private Rule parseModelGroupConfig(ModelGroupConfig config) { + final String analyticsKey = config.getAnalyticsKey(); + final String version = config.getVersion(); + final Schema schema = parseSchema(config.getSchema()); final List> rules = config.getRules().stream() - .map(this::toRuleConfig) + .map(this::parseRuleConfig) .toList(); final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); - return matchingRuleFactory.create(schema, ruleTree); + final Rule matchingRule = matchingRuleFactory.create(schema, ruleTree, analyticsKey, version); + final Rule defaultRule = new DefaultActionRule<>( + parseActions(config.getDefaultAction()), analyticsKey, version); + + return new AlternativeActionRule<>(matchingRule, defaultRule); } private Schema parseSchema(List schema) { - final Set names = schema.stream().map(SchemaFunctionConfig::getFunction).collect(Collectors.toSet()); - final List> schemaFunctions = schema.stream() .map(config -> SchemaFunctionHolder.of( + config.getFunction(), specification.schemaFunctionByName(config.getFunction()), config.getArgs())) .toList(); - return Schema.of(names, schemaFunctions); + return Schema.of(schemaFunctions); } - private RuleConfig toRuleConfig(AccountRuleConfig ruleConfig) { + private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); - final List> actions = ruleConfig.getResults().stream() + final List> actions = parseActions(ruleConfig.getResults()); + + return RuleConfig.of(ruleFired, actions); + } + + private List> parseActions(List functionConfigs) { + return functionConfigs.stream() .map(config -> RuleAction.of( specification.resultFunctionByName(config.getFunction()), config.getArgs())) .toList(); - - return RuleConfig.of(ruleFired, actions); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index a64f2e86c10..9dea74f9259 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -18,7 +18,7 @@ public class ModelGroupConfig { List schema; @JsonProperty("default") - RuleFunctionConfig defaultAction; + List defaultAction; List rules; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java new file mode 100644 index 00000000000..2af44206298 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java @@ -0,0 +1,32 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; + +public class AlternativeActionRule implements Rule { + + private final Rule delegate; + private final Rule alternative; + + public AlternativeActionRule(Rule delegate, Rule alternative) { + this.delegate = delegate; + this.alternative = alternative; + } + + public RuleResult process(T value) { + if (delegate == null && alternative == null) { + throw new NoMatchingRuleException(); + } else if (delegate == null) { + return alternative.process(value); + } + + try { + return delegate.process(value); + } catch (NoMatchingRuleException e) { + if (alternative != null) { + return alternative.process(value); + } + + throw e; + } + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java new file mode 100644 index 00000000000..50382d4733e --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -0,0 +1,40 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.apache.commons.collections4.ListUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; + +import java.util.Collections; +import java.util.List; + +public class DefaultActionRule implements Rule { + + private static final String RULE_NAME = "default"; + + private final List> actions; + + private final InfrastructureArguments infrastructureArguments; + + public DefaultActionRule(List> actions, String analyticsKey, String modelVersion) { + this.actions = ListUtils.emptyIfNull(actions); + + infrastructureArguments = InfrastructureArguments.of( + Collections.emptyMap(), analyticsKey, RULE_NAME, modelVersion); + } + + @Override + public RuleResult process(T value) { + return actions.stream().reduce( + RuleResult.unaltered(value), + (result, action) -> result.mergeWith(applyAction(action, result.getUpdateResult().getValue())), + RuleResult::mergeWith); + } + + private RuleResult applyAction(RuleAction action, T value) { + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + value, action.getConfigArguments(), infrastructureArguments); + + return action.getFunction().apply(arguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java index 1dd22261709..bc1c037c955 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java @@ -5,5 +5,8 @@ public interface MatchingRuleFactory { - Rule create(Schema schema, RuleTree> ruleTree); + Rule create(Schema schema, + RuleTree> ruleTree, + String analyticsKey, + String modelVersion); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java new file mode 100644 index 00000000000..e88dbe137c6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -0,0 +1,16 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import lombok.NoArgsConstructor; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.model.UpdateResult; + +import java.util.Collections; + +@NoArgsConstructor +public class NoOpRule implements Rule { + + @Override + public RuleResult process(T value) { + return RuleResult.of(UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList())); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index 14c128e7bbc..cd894e5380e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -6,6 +6,8 @@ import org.prebid.server.model.UpdateResult; import org.prebid.server.util.ListUtil; +import java.util.Collections; + @Value(staticConstructor = "of") public class RuleResult { @@ -20,4 +22,8 @@ public RuleResult mergeWith(RuleResult other) { return RuleResult.of(UpdateResult.of(updated, value), tags); } + + public static RuleResult unaltered(T value) { + return RuleResult.of(UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList())); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java new file mode 100644 index 00000000000..92ed27b49ed --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.exception; + +public class NoMatchingRuleException extends RuntimeException { + + public NoMatchingRuleException() { + super("No matching rule found"); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index 29c1e41dd86..86ed9874255 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -2,7 +2,7 @@ import com.iab.openrtb.request.BidRequest; import org.apache.commons.collections4.SetUtils; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -11,64 +11,91 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -import org.prebid.server.model.UpdateResult; -import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class RequestMatchingRule implements Rule { private final Schema schema; + private final Set schemaFunctionNames; private final RuleTree> ruleTree; - public RequestMatchingRule(Schema schema, RuleTree> ruleTree) { + private final String modelVersion; + private final String analyticsKey; + + public RequestMatchingRule(Schema schema, + RuleTree> ruleTree, + String modelVersion, + String analyticsKey) { + this.schema = Objects.requireNonNull(schema); + this.schemaFunctionNames = schema.getFunctions().stream() + .map(SchemaFunctionHolder::getName) + .collect(Collectors.toSet()); + this.ruleTree = Objects.requireNonNull(ruleTree); + this.modelVersion = StringUtils.defaultString(modelVersion); + this.analyticsKey = StringUtils.defaultString(analyticsKey); } @Override public RuleResult process(BidRequest bidRequest) { - return SetUtils.intersection(schema.getNames(), RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() + return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() ? processRule(bidRequest, null) : processPerImpRule(bidRequest); } private RuleResult processPerImpRule(BidRequest bidRequest) { return bidRequest.getImp().stream().reduce( - unalteredResult(bidRequest), + RuleResult.unaltered(bidRequest), (result, imp) -> result.mergeWith(processRule(result.getUpdateResult().getValue(), imp.getId())), RuleResult::mergeWith); } private RuleResult processRule(BidRequest bidRequest, String impId) { - final RequestPayload payload = RequestPayload.of(bidRequest, impId); - final List matchers = schema.getFunctions().stream() - .map(holder -> holder.getSchemaFunction() - .extract(SchemaFunctionArguments.of(payload, holder.getArguments()))) + final List> schemaFunctions = schema.getFunctions(); + + final List matchers = schemaFunctions.stream() + .map(holder -> holder.getSchemaFunction().extract( + SchemaFunctionArguments.of(RequestPayload.of(bidRequest, impId), holder.getArguments()))) .toList(); + final Map schemaFunctionResults = IntStream.range(0, matchers.size()) + .boxed() + .collect(Collectors.toMap(idx -> schemaFunctions.get(idx).getName(), matchers::get)); + final RuleConfig ruleConfig = ruleTree.getValue(matchers); - RuleResult result = unalteredResult(bidRequest); - for (RuleAction action : ruleConfig.getActions()) { - final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( - null, - "analyticsKey", - ruleConfig.getCondition(), - "modelVersion"); - final ResultFunctionArguments arguments = ResultFunctionArguments.of( - result.getUpdateResult().getValue(), action.getConfigArguments(), infrastructureArguments); + return ruleConfig.getActions().stream().reduce( + RuleResult.unaltered(bidRequest), + (result, action) -> result.mergeWith( + applyAction( + action, + result.getUpdateResult().getValue(), + schemaFunctionResults, + ruleConfig.getCondition())), + RuleResult::mergeWith); + } - result = result.mergeWith(action.getFunction().apply(arguments)); - } + private RuleResult applyAction(RuleAction action, + BidRequest bidRequest, + Map schemaFunctionResults, + String condition) { - return result; - } + final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( + schemaFunctionResults, analyticsKey, condition, modelVersion); + + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + bidRequest, action.getConfigArguments(), infrastructureArguments); - private static RuleResult unalteredResult(BidRequest bidRequest) { - return RuleResult.of(UpdateResult.unaltered(bidRequest), TagsImpl.of(Collections.emptyList())); + return action.getFunction().apply(arguments); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java index e57d0494688..67172405a59 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -8,7 +8,5 @@ @Value(staticConstructor = "of") public class Schema { - Set names; - List> functions; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java index f9f4fe4f32d..f074eb1659c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -8,6 +8,8 @@ @Value(staticConstructor = "of") public class SchemaFunctionHolder { + String name; + SchemaFunction schemaFunction; List arguments; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java index caee37c7a03..d697fe0a597 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; import org.apache.commons.lang3.ObjectUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import java.util.List; import java.util.Objects; @@ -19,17 +20,15 @@ public T getValue(List path) { for (String pathPart : path) { next = switch (next) { case RuleNode.LeafNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); - case RuleNode.IntermediateNode node -> - ObjectUtils.firstNonNull(node.next(pathPart), node.next("*")); - case null -> throw new IllegalArgumentException("Action absent"); + case RuleNode.IntermediateNode node -> ObjectUtils.firstNonNull(node.next(pathPart), node.next("*")); + case null -> throw new NoMatchingRuleException(); }; } return switch (next) { case RuleNode.LeafNode leaf -> leaf.value(); - case RuleNode.IntermediateNode ignored -> - throw new IllegalArgumentException("Argument count mismatch"); - case null -> throw new IllegalArgumentException("Action absent"); + case RuleNode.IntermediateNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); + case null -> throw new NoMatchingRuleException(); }; } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java index e536a16d6ad..c55833550ed 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java @@ -16,7 +16,7 @@ private WeightedEntry(double weight, T value) { this.value = value; if (weight < 0 || weight > MAX_WEIGHT) { - throw new IllegalArgumentException("Weight must be between 0 and 100"); + throw new IllegalArgumentException("Weight must be between 0 and " + MAX_WEIGHT); } } From 1325d5b299c8ea3747b6dd40debf48c9cfc6ad97 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 05:11:39 +0300 Subject: [PATCH 20/94] Refactored default action. --- .../engine/core/config/StageConfigParser.java | 50 +++++++++++++++---- .../core/rules/AlternativeActionRule.java | 12 +---- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index d3aa1962b3d..ab3fb1e5ecc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -11,6 +11,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; @@ -22,6 +23,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedEntry; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; +import org.springframework.util.CollectionUtils; import java.util.List; import java.util.Objects; @@ -64,21 +66,28 @@ private Rule parseModelGroupConfigs(List modelGr } private Rule parseModelGroupConfig(ModelGroupConfig config) { - final String analyticsKey = config.getAnalyticsKey(); - final String version = config.getVersion(); + final Rule matchingRule = parseMatchingRule(config); + final Rule defaultRule = parseDefaultActionRule(config); - final Schema schema = parseSchema(config.getSchema()); + return combineRules(matchingRule, defaultRule); + } + + private Rule parseMatchingRule(ModelGroupConfig config) { + final List schemaConfig = config.getSchema(); + final List rulesConfig = config.getRules(); + + if (CollectionUtils.isEmpty(schemaConfig) || CollectionUtils.isEmpty(rulesConfig)) { + return null; + } - final List> rules = config.getRules().stream() + final Schema schema = parseSchema(schemaConfig); + + final List> rules = rulesConfig.stream() .map(this::parseRuleConfig) .toList(); final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); - final Rule matchingRule = matchingRuleFactory.create(schema, ruleTree, analyticsKey, version); - final Rule defaultRule = new DefaultActionRule<>( - parseActions(config.getDefaultAction()), analyticsKey, version); - - return new AlternativeActionRule<>(matchingRule, defaultRule); + return matchingRuleFactory.create(schema, ruleTree, config.getAnalyticsKey(), config.getVersion()); } private Schema parseSchema(List schema) { @@ -98,10 +107,33 @@ private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { return RuleConfig.of(ruleFired, actions); } + private Rule parseDefaultActionRule(ModelGroupConfig config) { + final List defaultActionConfig = config.getDefaultAction(); + + if (CollectionUtils.isEmpty(config.getDefaultAction())) { + return null; + } + + return new DefaultActionRule<>( + parseActions(defaultActionConfig), config.getAnalyticsKey(), config.getVersion()); + } + private List> parseActions(List functionConfigs) { return functionConfigs.stream() .map(config -> RuleAction.of( specification.resultFunctionByName(config.getFunction()), config.getArgs())) .toList(); } + + private Rule combineRules(Rule left, Rule right) { + if (left == null && right == null) { + return new NoOpRule<>(); + } else if (left != null && right != null) { + return new AlternativeActionRule<>(left, right); + } else if (left != null) { + return left; + } + + return right; + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java index 2af44206298..2ce70c0a127 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java @@ -13,20 +13,10 @@ public AlternativeActionRule(Rule delegate, Rule alternative) { } public RuleResult process(T value) { - if (delegate == null && alternative == null) { - throw new NoMatchingRuleException(); - } else if (delegate == null) { - return alternative.process(value); - } - try { return delegate.process(value); } catch (NoMatchingRuleException e) { - if (alternative != null) { - return alternative.process(value); - } - - throw e; + return alternative.process(value); } } } From 3afa3f3ccb3a0058d2da6873c5580384fdde0876 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 05:46:36 +0300 Subject: [PATCH 21/94] Added checks for schema and tree dimensions, added ability to make weights not to sum up to less than 1. --- .../rule/engine/core/config/StageConfigParser.java | 9 ++++++++- .../rule/engine/core/rules/WeightedRule.java | 10 +++++++--- .../exception/InvalidMatcherConfiguration.java | 8 ++++++++ .../rules/exception/NoMatchingRuleException.java | 4 +++- .../rule/engine/core/rules/tree/RuleTree.java | 9 +++++++-- .../engine/core/rules/tree/RuleTreeFactory.java | 14 +++++++++++++- .../engine/core/util/NoMatchingValueException.java | 8 ++++++++ .../rule/engine/core/util/WeightedList.java | 9 ++++----- 8 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index ab3fb1e5ecc..c6d0026cc5c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -16,6 +16,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidMatcherConfiguration; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; @@ -62,7 +63,9 @@ private Rule parseModelGroupConfigs(List modelGr .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); - return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); + return new AlternativeActionRule<>( + new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)), + new NoOpRule<>()); } private Rule parseModelGroupConfig(ModelGroupConfig config) { @@ -87,6 +90,10 @@ private Rule parseMatchingRule(ModelGroupConfig config) { .toList(); final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); + if (schemaConfig.size() != ruleTree.getDepth()) { + throw new InvalidMatcherConfiguration("Schema functions count and rules matchers count mismatch"); + } + return matchingRuleFactory.create(schema, ruleTree, config.getAnalyticsKey(), config.getVersion()); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java index 98d491b93cd..5b8190d94bb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java @@ -1,5 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; +import org.prebid.server.hooks.modules.rule.engine.core.util.NoMatchingValueException; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import java.util.Objects; @@ -17,8 +19,10 @@ public WeightedRule(RandomGenerator random, WeightedList> weightedList) @Override public RuleResult process(T value) { - return weightedList.getForSeed(random.nextDouble()).process(value); - - // TODO: catch unmatched rule exception (add it) and try to run default action instead + try { + return weightedList.getForSeed(random.nextDouble()).process(value); + } catch (NoMatchingValueException e) { + throw new NoMatchingRuleException(); + } } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java new file mode 100644 index 00000000000..e7fea8db54d --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.exception; + +public class InvalidMatcherConfiguration extends RuntimeException { + + public InvalidMatcherConfiguration(String message) { + super(message); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java index 92ed27b49ed..fc1869d3d14 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java @@ -1,6 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.exception; -public class NoMatchingRuleException extends RuntimeException { +import org.prebid.server.hooks.modules.rule.engine.core.util.NoMatchingValueException; + +public class NoMatchingRuleException extends NoMatchingValueException { public NoMatchingRuleException() { super("No matching rule found"); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java index d697fe0a597..d95982d9e91 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java @@ -1,5 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; +import lombok.Getter; import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; @@ -8,10 +9,14 @@ public class RuleTree { - RuleNode root; + private final RuleNode root; - public RuleTree(RuleNode root) { + @Getter + private final int depth; + + public RuleTree(RuleNode root, int depth) { this.root = Objects.requireNonNull(root); + this.depth = depth; } public T getValue(List path) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 8bd951e2056..3f794380cae 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidMatcherConfiguration; import java.util.List; import java.util.Map; @@ -10,7 +11,14 @@ public class RuleTreeFactory { public static RuleTree> buildTree(List> rules) { - return new RuleTree<>(parseRuleNode(toParsingContexts(rules))); + final List>> parsingContexts = toParsingContexts(rules); + final int depth = getDepth(parsingContexts); + + if (!parsingContexts.stream().allMatch(context -> context.argumentMatchers().size() == depth)) { + throw new InvalidMatcherConfiguration("Mismatched arguments count"); + } + + return new RuleTree<>(parseRuleNode(parsingContexts), depth); } private static List>> toParsingContexts(List> rules) { @@ -21,6 +29,10 @@ private static List>> toParsingContexts(List int getDepth(List> contexts) { + return contexts.isEmpty() ? 0 : contexts.getFirst().argumentMatchers().size(); + } + private static RuleNode parseRuleNode(List> parsingContexts) { if (parsingContexts.size() == 1 && parsingContexts.getFirst().argumentMatchers().isEmpty()) { return new RuleNode.LeafNode<>(parsingContexts.getFirst().value); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java new file mode 100644 index 00000000000..3840cc14ff0 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +public class NoMatchingValueException extends RuntimeException { + + public NoMatchingValueException(String message) { + super(message); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java index c01fe8fa666..239b9b5f274 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java @@ -24,9 +24,9 @@ private void validateEntries(List> entries) { } final double sum = entries.stream().mapToDouble(WeightedEntry::getWeight).sum(); - if (Math.abs(sum - WeightedEntry.MAX_WEIGHT) > EPSILON) { + if (sum > WeightedEntry.MAX_WEIGHT + EPSILON) { throw new IllegalArgumentException( - "Weighted list weights sum must be equal to " + WeightedEntry.MAX_WEIGHT); + "Weighted list weights sum must be less than " + WeightedEntry.MAX_WEIGHT); } } @@ -44,7 +44,7 @@ private List> prepareEntries(List> entries) { public T getForSeed(double seed) { if (seed < 0 || seed > WeightedEntry.MAX_WEIGHT) { - throw new IllegalArgumentException("Seed number must be between 0 and 100"); + throw new IllegalArgumentException("Seed number must be between 0 and " + WeightedEntry.MAX_WEIGHT); } for (WeightedEntry entry : entries) { @@ -53,7 +53,6 @@ public T getForSeed(double seed) { } } - // Should never occur - throw new IllegalStateException("No entry found for seed " + seed); + throw new NoMatchingValueException("No entry found for seed " + seed); } } From b71803f08c8d3a630fc0158ce35045be12f83bc7 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 06:00:02 +0300 Subject: [PATCH 22/94] Fixed bug when unmatched rule gets translated to NoOp. --- .../modules/rule/engine/core/config/StageConfigParser.java | 4 +--- .../hooks/modules/rule/engine/core/rules/WeightedRule.java | 3 +-- .../engine/core/rules/request/RequestMatchingRule.java | 7 ++++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index c6d0026cc5c..84a6f5318a2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -63,9 +63,7 @@ private Rule parseModelGroupConfigs(List modelGr .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); - return new AlternativeActionRule<>( - new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)), - new NoOpRule<>()); + return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } private Rule parseModelGroupConfig(ModelGroupConfig config) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java index 5b8190d94bb..74d9cba3efe 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java @@ -1,6 +1,5 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; -import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import org.prebid.server.hooks.modules.rule.engine.core.util.NoMatchingValueException; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; @@ -22,7 +21,7 @@ public RuleResult process(T value) { try { return weightedList.getForSeed(random.nextDouble()).process(value); } catch (NoMatchingValueException e) { - throw new NoMatchingRuleException(); + return RuleResult.unaltered(value); } } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index 86ed9874255..82ae675bdd2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -14,6 +14,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -70,7 +71,11 @@ private RuleResult processRule(BidRequest bidRequest, String impId) final Map schemaFunctionResults = IntStream.range(0, matchers.size()) .boxed() - .collect(Collectors.toMap(idx -> schemaFunctions.get(idx).getName(), matchers::get)); + .collect(Collectors.toMap( + idx -> schemaFunctions.get(idx).getName(), + matchers::get, + (left, right) -> left, + HashMap::new)); final RuleConfig ruleConfig = ruleTree.getValue(matchers); From c4d34e28f62db74f672d7fc7c0d2701929556afb Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 1 May 2025 06:12:30 +0300 Subject: [PATCH 23/94] Added functions config arguments validations. --- .../rule/engine/core/config/StageConfigParser.java | 10 +++++++++- .../engine/core/rules/request/RequestMatchingRule.java | 9 ++++----- .../schema/functions/DeviceCountryFunction.java | 2 +- .../rule/engine/core/rules/schema/SchemaFunction.java | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 84a6f5318a2..a21faabaead 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -102,6 +102,9 @@ private Schema parseSchema(List schema) { specification.schemaFunctionByName(config.getFunction()), config.getArgs())) .toList(); + schemaFunctions.forEach(holder -> + holder.getSchemaFunction().validateConfigArguments(holder.getArguments())); + return Schema.of(schemaFunctions); } @@ -124,10 +127,15 @@ private Rule parseDefaultActionRule(ModelGroupConfig config) { } private List> parseActions(List functionConfigs) { - return functionConfigs.stream() + final List> actions = functionConfigs.stream() .map(config -> RuleAction.of( specification.resultFunctionByName(config.getFunction()), config.getArgs())) .toList(); + + actions.forEach(action -> + action.getFunction().validateConfigArguments(action.getConfigArguments())); + + return actions; } private Rule combineRules(Rule left, Rule right) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index 82ae675bdd2..f7126a0805d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -14,7 +14,6 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -24,6 +23,8 @@ public class RequestMatchingRule implements Rule { + private static final String NULL_MATCHER = "null"; + private final Schema schema; private final Set schemaFunctionNames; private final RuleTree> ruleTree; @@ -67,15 +68,13 @@ private RuleResult processRule(BidRequest bidRequest, String impId) final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( SchemaFunctionArguments.of(RequestPayload.of(bidRequest, impId), holder.getArguments()))) + .map(matcher -> StringUtils.defaultIfEmpty(matcher, NULL_MATCHER)) .toList(); final Map schemaFunctionResults = IntStream.range(0, matchers.size()) .boxed() .collect(Collectors.toMap( - idx -> schemaFunctions.get(idx).getName(), - matchers::get, - (left, right) -> left, - HashMap::new)); + idx -> schemaFunctions.get(idx).getName(), matchers::get, (left, right) -> left)); final RuleConfig ruleConfig = ruleTree.getValue(matchers); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java index e54f94e9d9a..ea44bc9e193 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java @@ -36,7 +36,7 @@ public String extract(SchemaFunctionArguments arguments) { } @Override - public void validate(List configArguments) { + public void validateConfigArguments(List configArguments) { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 781adb3dc7a..5c86c6f9b27 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -8,5 +8,5 @@ public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); - void validate(List configArguments); + void validateConfigArguments(List configArguments); } From 5ea75dae1bd0628e7c14e80bfb5fa02a1b1b5124 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Sun, 4 May 2025 14:47:50 +0300 Subject: [PATCH 24/94] Styling fixes. --- .../hooks/modules/rule/engine/core/cache/RuleRegistry.java | 1 - .../modules/rule/engine/core/config/StageConfigParser.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java index 8e4c785b0bc..2290d589cb9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java @@ -16,7 +16,6 @@ public class RuleRegistry { private final AccountConfigParser parser; private final Vertx vertx; - private final ConcurrentMap> accountIdToRules; public RuleRegistry(long cacheExpireAfterMinutes, diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index a21faabaead..eac2516939b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -99,7 +99,8 @@ private Schema parseSchema(List schema) { final List> schemaFunctions = schema.stream() .map(config -> SchemaFunctionHolder.of( config.getFunction(), - specification.schemaFunctionByName(config.getFunction()), config.getArgs())) + specification.schemaFunctionByName(config.getFunction()), + config.getArgs())) .toList(); schemaFunctions.forEach(holder -> From b9baf8efdffa583fc2e8916c2342880fa8805e75 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 21 May 2025 14:35:51 +0300 Subject: [PATCH 25/94] WIP. --- .../rule/engine/core/rules/RuleResult.java | 2 +- .../core/rules/AlternativeActionRuleTest.java | 64 +++++++++++++++ .../core/rules/DefaultActionRuleTest.java | 78 +++++++++++++++++++ .../src/test/core/rules/WeightedRuleTest.java | 36 +++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java create mode 100644 extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java create mode 100644 extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index cd894e5380e..789c97b92f4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -18,7 +18,7 @@ public class RuleResult { public RuleResult mergeWith(RuleResult other) { final boolean updated = other.updateResult.isUpdated() || updateResult.isUpdated(); final T value = other.updateResult.getValue(); - final Tags tags = TagsImpl.of(ListUtil.union(other.analyticsTags.activities(), analyticsTags.activities())); + final Tags tags = TagsImpl.of(ListUtil.union(analyticsTags.activities(), other.analyticsTags.activities())); return RuleResult.of(UpdateResult.of(updated, value), tags); } diff --git a/extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java new file mode 100644 index 00000000000..6bd2ed9fce1 --- /dev/null +++ b/extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java @@ -0,0 +1,64 @@ +package core.rules; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +public class AlternativeActionRuleTest { + + private static final RuleResult DELEGATE_RESULT = RuleResult.unaltered(new Object()); + private static final RuleResult ALTERNATIVE_RESULT = RuleResult.unaltered(new Object()); + + @Mock(strictness = LENIENT) + private Rule delegate; + + @Mock(strictness = LENIENT) + private Rule alternative; + + private AlternativeActionRule target; + + @BeforeEach + public void setUp() { + target = new AlternativeActionRule<>(delegate, alternative); + } + + @Test + public void processShouldReturnDelegateResult() { + // given + given(delegate.process(any())).willReturn(DELEGATE_RESULT); + given(alternative.process(any())).willReturn(ALTERNATIVE_RESULT); + + // when + final RuleResult result = target.process(new Object()); + + // then + assertThat(result).isEqualTo(DELEGATE_RESULT); + verifyNoInteractions(alternative); + } + + @Test + public void processShouldReturnAlternativeResultWhenNoMatchingRuleException() { + // given + given(delegate.process(any())).willThrow(new NoMatchingRuleException()); + given(alternative.process(any())).willReturn(ALTERNATIVE_RESULT); + + // when + final RuleResult result = target.process(new Object()); + + // then + assertThat(result).isEqualTo(ALTERNATIVE_RESULT); + } +} diff --git a/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java new file mode 100644 index 00000000000..34f258356a7 --- /dev/null +++ b/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java @@ -0,0 +1,78 @@ +package core.rules; + +import org.junit.jupiter.api.Test; +import org.mockito.stubbing.Answer; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class DefaultActionRuleTest { + + private static final Object VALUE = new Object(); + + @Test + public void processShouldAccumulateResultFromAllRuleActions() { + // given + final RuleAction firstAction = (RuleAction) mock(RuleAction.class); + final ResultFunction firstFunction = (ResultFunction) mock(ResultFunction.class); + + given(firstFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("firstAction")); + given(firstAction.getFunction()).willReturn(firstFunction); + given(firstAction.getConfigArguments()).willReturn(emptyList()); + + + final RuleAction secondAction = (RuleAction) mock(RuleAction.class); + final ResultFunction secondFunction = (ResultFunction) mock(ResultFunction.class); + + given(secondFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("secondAction")); + given(secondAction.getFunction()).willReturn(secondFunction); + given(secondAction.getConfigArguments()).willReturn(emptyList()); + + final DefaultActionRule target = new DefaultActionRule<>( + asList(firstAction, secondAction), "analyticsKey", "modelVersion"); + + // when + final RuleResult result = target.process(VALUE); + + // then + final Tags expectedTags = TagsImpl.of( + asList(ActivityImpl.of("firstAction", "success", emptyList()), + ActivityImpl.of("secondAction", "success", emptyList()))); + + assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); + + verify(firstFunction).apply( + ResultFunctionArguments.of( + VALUE, + emptyList(), + InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); + verify(secondFunction).apply( + ResultFunctionArguments.of( + VALUE, + emptyList(), + InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); + } + + private static Answer> prepareResultFunctionAnswer(String activityName) { + return invocation -> RuleResult.of( + UpdateResult.updated(((ResultFunctionArguments) invocation.getArgument(0)).getOperand()), + TagsImpl.of(singletonList(ActivityImpl.of(activityName, "success", emptyList())))); + } +} diff --git a/extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java new file mode 100644 index 00000000000..645bf335979 --- /dev/null +++ b/extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java @@ -0,0 +1,36 @@ +package core.rules; + +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; + +import java.util.random.RandomGenerator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +public class WeightedRuleTest { + + @Test + public void processShouldUtilizeRuleFromWeightedList() { + // given + final WeightedList> ruleList = (WeightedList>) mock(WeightedList.class); + final RuleResult stub = RuleResult.unaltered(new Object()); + given(ruleList.getForSeed(anyDouble())).willReturn(value -> stub); + + final RandomGenerator randomGenerator = mock(RandomGenerator.class); + given(randomGenerator.nextDouble()).willReturn(0.5); + + final WeightedRule rule = new WeightedRule<>(randomGenerator, ruleList); + + // when + final RuleResult result = rule.process(rule); + + // then + assertThat(result).isEqualTo(stub); + } +} From 4c73213420d22f383e85a8a036eaf36f41a3f587 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 21 May 2025 15:06:03 +0300 Subject: [PATCH 26/94] WIP. --- .../rule/engine/core/rules/CompositeRule.java | 20 ++----- .../test/core/rules/CompositeRuleTest.java | 55 +++++++++++++++++++ .../test/core/rules/tree/RuleTreeTest.java | 38 +++++++++++++ 3 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java create mode 100644 extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java index 81ad45ea0df..08eed584ee6 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java @@ -1,11 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.Value; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.v1.analytics.Activity; -import org.prebid.server.model.UpdateResult; -import java.util.ArrayList; import java.util.List; @Value(staticConstructor = "of") @@ -15,17 +11,9 @@ public class CompositeRule implements Rule { @Override public RuleResult process(T value) { - final List activities = new ArrayList<>(); - T modifiedValue = value; - boolean updated = false; - - for (Rule subrule : subrules) { - final RuleResult subresult = subrule.process(modifiedValue); - modifiedValue = subresult.getUpdateResult().getValue(); - updated = updated | subresult.getUpdateResult().isUpdated(); - activities.addAll(subresult.getAnalyticsTags().activities()); - } - - return RuleResult.of(UpdateResult.of(updated, modifiedValue), TagsImpl.of(activities)); + return subrules.stream().reduce( + RuleResult.unaltered(value), + (result, rule) -> result.mergeWith(rule.process(result.getUpdateResult().getValue())), + RuleResult::mergeWith); } } diff --git a/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java new file mode 100644 index 00000000000..ba3560bd2be --- /dev/null +++ b/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java @@ -0,0 +1,55 @@ +package core.rules; + +import org.junit.jupiter.api.Test; +import org.mockito.stubbing.Answer; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class CompositeRuleTest { + + private static final Object VALUE = new Object(); + + @Test + public void processShouldAccumulateResultFromAllSubrules() { + // given + final Rule firstRule = (Rule) mock(Rule.class); + given(firstRule.process(any())).willAnswer(invocationOnMock -> RuleResult.of( + UpdateResult.updated(invocationOnMock.getArgument(0)), + TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))))); + + final Rule secondRule = (Rule) mock(Rule.class); + given(secondRule.process(any())).willAnswer(invocationOnMock -> RuleResult.of( + UpdateResult.updated(invocationOnMock.getArgument(0)), + TagsImpl.of(singletonList(ActivityImpl.of("secondActivity", "success", emptyList()))))); + + final CompositeRule target = CompositeRule.of(asList(firstRule, secondRule)); + + // when + final RuleResult result = target.process(VALUE); + + // then + final Tags expectedTags = TagsImpl.of( + asList(ActivityImpl.of("firstActivity", "success", emptyList()), + ActivityImpl.of("secondActivity", "success", emptyList()))); + + assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); + + verify(firstRule).process(VALUE); + verify(secondRule).process(VALUE); + } +} diff --git a/extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java b/extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java new file mode 100644 index 00000000000..e79e6042aff --- /dev/null +++ b/extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java @@ -0,0 +1,38 @@ +package core.rules.tree; + +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleNode; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; + +import java.util.Map; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class RuleTreeTest { + + @Test + public void getValueShouldReturnExpectedValue() { + // given + final Map> subnodes = Map.of( + "A", + new RuleNode.IntermediateNode<>( + Map.of("B", new RuleNode.LeafNode<>("AB"), "*", new RuleNode.LeafNode<>("AC"))), + "B", + new RuleNode.IntermediateNode<>( + Map.of("B", new RuleNode.LeafNode<>("BB"), "C", new RuleNode.LeafNode<>("BC")))); + + final RuleTree tree = new RuleTree<>(new RuleNode.IntermediateNode<>(subnodes), 2); + + // when and then + assertThat(tree.getValue(asList("A", "B"))).isEqualTo("AB"); + assertThat(tree.getValue(asList("A", "C"))).isEqualTo("AC"); + assertThat(tree.getValue(asList("B", "B"))).isEqualTo("BB"); + assertThat(tree.getValue(asList("B", "C"))).isEqualTo("BC"); + assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.getValue(asList("C", "B"))); + assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.getValue(singletonList("C"))); + } +} From 570d097bd4867812b5ba358ede0a4ff1a2172ace Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 21 May 2025 16:30:45 +0300 Subject: [PATCH 27/94] Started writing schema functions. --- .../config/RuleEngineModuleConfiguration.java | 11 +-- .../core/config/AccountConfigParser.java | 6 +- ...equestPayload.java => RequestContext.java} | 4 +- .../rules/request/RequestMatchingRule.java | 14 ++-- .../rules/request/RequestSpecification.java | 17 +++-- .../functions/IncludeBiddersFunction.java | 33 --------- .../functions/DeviceCountryFunction.java | 42 ----------- .../functions/RequestSchemaFunctions.java | 72 +++++++++++++++++++ .../core/rules/schema/AvailableFunction.java | 24 +++++++ .../core/rules/schema/ExtractingFunction.java | 24 +++++++ .../engine/core/rules/schema/InFunction.java | 28 ++++++++ .../core/rules/schema/SchemaFunction.java | 5 +- .../ConfigurationValidationException.java | 8 +++ .../engine/core/util/ValidationUtils.java | 22 ++++++ 14 files changed, 212 insertions(+), 98 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/{RequestPayload.java => RequestContext.java} (80%) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 29c41dcf996..8ed643f6fe4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -6,8 +6,8 @@ import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestMatchingRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; @@ -28,17 +28,20 @@ RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { } @Bean - StageConfigParser processedAuctionRequestStageParser() { + StageConfigParser processedAuctionRequestStageParser( + @Value("${datacenter-region:#{null}}") String datacenterRegion) { + return new StageConfigParser<>( () -> ThreadLocalRandom.current().nextLong(), Stage.processed_auction_request, RequestSpecification.INSTANCE, - RequestMatchingRule::new); + (schema, ruleTree, modelVersion, analyticsKey) -> + new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); } @Bean AccountConfigParser accountConfigParser( - StageConfigParser processedAuctionRequestStageParser) { + StageConfigParser processedAuctionRequestStageParser) { return new AccountConfigParser(ObjectMapperProvider.mapper(), processedAuctionRequestStageParser); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index 06ceaa40680..175418a28f8 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -7,17 +7,17 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; import java.util.Objects; public class AccountConfigParser { private final ObjectMapper mapper; - private final StageConfigParser processedAuctionRequestStageParser; + private final StageConfigParser processedAuctionRequestStageParser; public AccountConfigParser(ObjectMapper mapper, - StageConfigParser processedAuctionRequestStageParser) { + StageConfigParser processedAuctionRequestStageParser) { this.mapper = Objects.requireNonNull(mapper); this.processedAuctionRequestStageParser = Objects.requireNonNull(processedAuctionRequestStageParser); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java similarity index 80% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java index 54ddd04006f..069002a74e7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestPayload.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java @@ -4,9 +4,11 @@ import lombok.Value; @Value(staticConstructor = "of") -public class RequestPayload { +public class RequestContext { BidRequest bidRequest; String impId; + + String datacenter; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java index f7126a0805d..7077c7dec03 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java @@ -25,17 +25,19 @@ public class RequestMatchingRule implements Rule { private static final String NULL_MATCHER = "null"; - private final Schema schema; + private final Schema schema; private final Set schemaFunctionNames; private final RuleTree> ruleTree; private final String modelVersion; private final String analyticsKey; + private final String datacenter; - public RequestMatchingRule(Schema schema, + public RequestMatchingRule(Schema schema, RuleTree> ruleTree, String modelVersion, - String analyticsKey) { + String analyticsKey, + String datacenter) { this.schema = Objects.requireNonNull(schema); this.schemaFunctionNames = schema.getFunctions().stream() @@ -45,6 +47,7 @@ public RequestMatchingRule(Schema schema, this.ruleTree = Objects.requireNonNull(ruleTree); this.modelVersion = StringUtils.defaultString(modelVersion); this.analyticsKey = StringUtils.defaultString(analyticsKey); + this.datacenter = StringUtils.defaultString(datacenter); } @Override @@ -63,11 +66,12 @@ private RuleResult processPerImpRule(BidRequest bidRequest) { } private RuleResult processRule(BidRequest bidRequest, String impId) { - final List> schemaFunctions = schema.getFunctions(); + final RequestContext context = RequestContext.of(bidRequest, impId, datacenter); + final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(RequestPayload.of(bidRequest, impId), holder.getArguments()))) + SchemaFunctionArguments.of(context, holder.getArguments()))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, NULL_MATCHER)) .toList(); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java index 95771367fd3..887d4a2d768 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java @@ -5,8 +5,7 @@ import lombok.NoArgsConstructor; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions.IncludeBiddersFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions.DeviceCountryFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions.RequestSchemaFunctions; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; @@ -14,20 +13,20 @@ import java.util.Set; @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class RequestSpecification implements StageSpecification { +public class RequestSpecification implements StageSpecification { public static final RequestSpecification INSTANCE = new RequestSpecification(); public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(); - private static final Map> SCHEMA_FUNCTIONS = Map.of( - DeviceCountryFunction.NAME, DeviceCountryFunction.INSTANCE); + private static final Map> SCHEMA_FUNCTIONS = Map.of( + RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION, + RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION); - private static final Map> RESULT_FUNCTIONS = Map.of( - IncludeBiddersFunction.NAME, IncludeBiddersFunction.INSTANCE); + private static final Map> RESULT_FUNCTIONS = Map.of(); - public SchemaFunction schemaFunctionByName(String name) { - final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); + public SchemaFunction schemaFunctionByName(String name) { + final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java deleted file mode 100644 index 9de13f717bc..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/result/functions/IncludeBiddersFunction.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request.result.functions; - -import com.fasterxml.jackson.databind.JsonNode; -import com.iab.openrtb.request.BidRequest; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.model.UpdateResult; - -import java.util.Collections; -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class IncludeBiddersFunction implements ResultFunction { - - public static final IncludeBiddersFunction INSTANCE = new IncludeBiddersFunction(); - - public static final String NAME = "includeBidders"; - - @Override - public RuleResult apply(ResultFunctionArguments arguments) { - return RuleResult.of( - UpdateResult.unaltered(arguments.getOperand()), - TagsImpl.of(Collections.emptyList())); - } - - @Override - public void validateConfigArguments(List configArguments) { - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java deleted file mode 100644 index ea44bc9e193..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/DeviceCountryFunction.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions; - -import com.fasterxml.jackson.databind.JsonNode; -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Geo; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.apache.commons.collections4.CollectionUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestPayload; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; - -import java.util.List; -import java.util.Optional; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class DeviceCountryFunction implements SchemaFunction { - - public static final String NAME = "deviceCountry"; - - public static final DeviceCountryFunction INSTANCE = new DeviceCountryFunction(); - - @Override - public String extract(SchemaFunctionArguments arguments) { - final BidRequest bidRequest = arguments.getOperand().getBidRequest(); - final List args = arguments.getConfigArguments(); - if (CollectionUtils.isEmpty(args)) { - return Optional.ofNullable(bidRequest.getDevice()) - .map(Device::getGeo) - .map(Geo::getCountry) - .orElse(null); - } - - return "true"; - } - - @Override - public void validateConfigArguments(List configArguments) { - - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java new file mode 100644 index 00000000000..b271a96974a --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java @@ -0,0 +1,72 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import com.iab.openrtb.request.User; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.ObjectUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.AvailableFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.ExtractingFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.InFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +@UtilityClass +public class RequestSchemaFunctions { + + public static final String DEVICE_COUNTRY_FUNCTION_NAME = "deviceCountry"; + public static final String DEVICE_COUNTRY_IN_FUNCTION_NAME = "deviceCountryIn"; + public static final String DATACENTER_FUNCTION_NAME = "datacenter"; + public static final String DATACENTER_IN_FUNCTION_NAME = "datacenterIn"; + public static final String CHANNEL_FUNCTION_NAME = "channel"; + public static final String EID_AVAILABLE_FUNCTION_NAME = "eidAvailable"; + + public static final SchemaFunction DEVICE_COUNTRY_FUNCTION = ExtractingFunction.of( + RequestSchemaFunctions::extractDeviceCountry); + public static final SchemaFunction DEVICE_COUNTRY_IN_FUNCTION = InFunction.of( + RequestSchemaFunctions::extractDeviceCountry); + public static final SchemaFunction DATACENTER_FUNCTION = ExtractingFunction.of( + RequestSchemaFunctions::extractDataCenter); + public static final SchemaFunction DATACENTER_IN_FUNCTION = InFunction.of( + RequestSchemaFunctions::extractDataCenter); + public static final SchemaFunction CHANNEL_FUNCTION = ExtractingFunction.of( + RequestSchemaFunctions::extractChannel); + public static final SchemaFunction EID_AVAILABLE_FUNCTION = AvailableFunction.of( + RequestSchemaFunctions::isEidAvailable); + + private static String extractDeviceCountry(RequestContext context) { + return Optional.ofNullable(context.getBidRequest().getDevice()) + .map(Device::getGeo) + .map(Geo::getCountry) + .orElse(SchemaFunction.NULL_RESULT); + } + + private static String extractDataCenter(RequestContext context) { + return ObjectUtils.defaultIfNull(context.getDatacenter(), SchemaFunction.NULL_RESULT); + } + + private static String extractChannel(RequestContext context) { + return Optional.of(context.getBidRequest()) + .map(BidRequest::getExt) + .map(ExtRequest::getPrebid) + .map(ExtRequestPrebid::getChannel) + .map(ExtRequestPrebidChannel::getName) + .orElse(SchemaFunction.NULL_RESULT); + } + + private static boolean isEidAvailable(RequestContext context) { + return Optional.of(context.getBidRequest()) + .map(BidRequest::getUser) + .map(User::getEids) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java new file mode 100644 index 00000000000..fcd009fda55 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java @@ -0,0 +1,24 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.List; +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class AvailableFunction implements SchemaFunction { + + Function isAvailable; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return Boolean.toString(isAvailable.apply(arguments.getOperand())); + } + + @Override + public void validateConfigArguments(List configArguments) { + ValidationUtils.assertNoArgs(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java new file mode 100644 index 00000000000..ab57ecd8acc --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java @@ -0,0 +1,24 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.List; +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class ExtractingFunction implements SchemaFunction { + + Function extractor; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return extractor.apply(arguments.getOperand()); + } + + @Override + public void validateConfigArguments(List configArguments) { + ValidationUtils.assertNoArgs(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java new file mode 100644 index 00000000000..99c481bb582 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java @@ -0,0 +1,28 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.List; +import java.util.function.Function; + +@Value(staticConstructor = "of") +public class InFunction implements SchemaFunction { + + Function extractor; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final String matchingValue = extractor.apply(arguments.getOperand()); + final boolean isValueInList = arguments.getConfigArguments().stream() + .anyMatch(argument -> argument.asText().equals(matchingValue)); + + return Boolean.toString(isValueInList); + } + + @Override + public void validateConfigArguments(List configArguments) { + ValidationUtils.assertArrayOfStrings(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 5c86c6f9b27..89e92a14065 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -6,7 +6,10 @@ public interface SchemaFunction { + String NULL_RESULT = "null"; + String extract(SchemaFunctionArguments arguments); - void validateConfigArguments(List configArguments); + default void validateConfigArguments(List configArguments) { + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java new file mode 100644 index 00000000000..6c5c194d3e1 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java @@ -0,0 +1,8 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +public class ConfigurationValidationException extends RuntimeException { + + public ConfigurationValidationException(String message) { + super(message); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java new file mode 100644 index 00000000000..7356bd8a9f6 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java @@ -0,0 +1,22 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +public class ValidationUtils { + + public static void assertArrayOfStrings(List configurationArguments) { + if (CollectionUtils.isEmpty(configurationArguments) + || !configurationArguments.stream().allMatch(JsonNode::isTextual)) { + throw new ConfigurationValidationException("Array of strings required"); + } + } + + public static void assertNoArgs(List configurationArguments) { + if (configurationArguments != null) { + throw new ConfigurationValidationException("No arguments allowed"); + } + } +} From 5c3599ae27cb7d60c82f5822b4be087b313e3b7e Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 29 May 2025 18:22:27 +0300 Subject: [PATCH 28/94] Added adUnitCode function. --- .../config/RuleEngineModuleConfiguration.java | 11 +-- .../core/config/AccountConfigParser.java | 10 ++- .../engine/core/config/StageConfigParser.java | 52 ++++++------- .../core/config/model/AccountRuleConfig.java | 2 +- .../core/config/model/ModelGroupConfig.java | 2 +- ...nConfig.java => ResultFunctionConfig.java} | 6 +- .../{rules => }/request/RequestContext.java | 2 +- .../request/RequestMatchingRule.java | 39 +++++----- .../request/RequestSpecification.java | 24 ++++-- .../functions/ExcludeBiddersFunction.java | 44 +++++++++++ .../ExcludeBiddersFunctionConfig.java | 20 +++++ .../schema/functions/AdUnitCodeFunction.java | 73 +++++++++++++++++++ .../schema/functions/EidInFunction.java | 21 ++++++ .../functions/RequestSchemaFunctions.java | 12 +-- .../core/rules/AlternativeActionRule.java | 14 ++-- .../rule/engine/core/rules/CompositeRule.java | 8 +- .../engine/core/rules/DefaultActionRule.java | 29 +++++--- .../core/rules/MatchingRuleFactory.java | 10 +-- .../rule/engine/core/rules/NoOpRule.java | 5 +- .../rule/engine/core/rules/PerStageRule.java | 3 +- .../modules/rule/engine/core/rules/Rule.java | 4 +- .../rule/engine/core/rules/RuleConfig.java | 4 +- .../engine/core/rules/StageSpecification.java | 4 +- .../rule/engine/core/rules/WeightedRule.java | 10 +-- .../rules/result/InfrastructureArguments.java | 5 +- .../core/rules/result/ResultFunction.java | 8 +- .../rules/result/ResultFunctionArguments.java | 8 +- .../engine/core/rules/result/RuleAction.java | 8 +- .../core/rules/schema/SchemaFunction.java | 2 +- .../core/rules/tree/RuleTreeFactory.java | 6 +- ...RuleEngineProcessedAuctionRequestHook.java | 11 ++- .../core/rules/DefaultActionRuleTest.java | 4 +- 32 files changed, 323 insertions(+), 138 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/{RuleFunctionConfig.java => ResultFunctionConfig.java} (70%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{rules => }/request/RequestContext.java (73%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{rules => }/request/RequestMatchingRule.java (72%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{rules => }/request/RequestSpecification.java (56%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{rules => }/request/schema/functions/RequestSchemaFunctions.java (87%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 8ed643f6fe4..5743af1e761 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -2,13 +2,14 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Vertx; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestMatchingRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestSpecification; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.beans.factory.annotation.Value; @@ -28,7 +29,7 @@ RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { } @Bean - StageConfigParser processedAuctionRequestStageParser( + StageConfigParser processedAuctionRequestStageParser( @Value("${datacenter-region:#{null}}") String datacenterRegion) { return new StageConfigParser<>( @@ -41,7 +42,7 @@ StageConfigParser processedAuctionRequestStageParser @Bean AccountConfigParser accountConfigParser( - StageConfigParser processedAuctionRequestStageParser) { + StageConfigParser processedAuctionRequestStageParser) { return new AccountConfigParser(ObjectMapperProvider.mapper(), processedAuctionRequestStageParser); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index 175418a28f8..cb1ecbd117d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -4,20 +4,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import java.util.Objects; public class AccountConfigParser { private final ObjectMapper mapper; - private final StageConfigParser processedAuctionRequestStageParser; + private final StageConfigParser processedAuctionRequestStageParser; - public AccountConfigParser(ObjectMapper mapper, - StageConfigParser processedAuctionRequestStageParser) { + public AccountConfigParser( + ObjectMapper mapper, + StageConfigParser processedAuctionRequestStageParser) { this.mapper = Objects.requireNonNull(mapper); this.processedAuctionRequestStageParser = Objects.requireNonNull(processedAuctionRequestStageParser); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index eac2516939b..96f9cfc6f83 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -4,7 +4,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountRuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; -import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleFunctionConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.ResultFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; @@ -30,17 +30,17 @@ import java.util.Objects; import java.util.random.RandomGenerator; -public class StageConfigParser { +public class StageConfigParser { private final RandomGenerator randomGenerator; - private final StageSpecification specification; + private final StageSpecification specification; private final Stage stage; - private final MatchingRuleFactory matchingRuleFactory; + private final MatchingRuleFactory matchingRuleFactory; public StageConfigParser(RandomGenerator randomGenerator, Stage stage, - StageSpecification specification, - MatchingRuleFactory matchingRuleFactory) { + StageSpecification specification, + MatchingRuleFactory matchingRuleFactory) { this.randomGenerator = Objects.requireNonNull(randomGenerator); this.stage = Objects.requireNonNull(stage); @@ -48,8 +48,8 @@ public StageConfigParser(RandomGenerator randomGenerator, this.matchingRuleFactory = Objects.requireNonNull(matchingRuleFactory); } - public Rule parse(AccountConfig config) { - final List> stageSubrules = config.getRuleSets().stream() + public Rule parse(AccountConfig config) { + final List> stageSubrules = config.getRuleSets().stream() .filter(ruleSet -> stage.equals(ruleSet.getStage())) .map(RuleSetConfig::getModelGroups) .map(this::parseModelGroupConfigs) @@ -58,22 +58,22 @@ public Rule parse(AccountConfig config) { return CompositeRule.of(stageSubrules); } - private Rule parseModelGroupConfigs(List modelGroupConfigs) { - final List>> weightedRules = modelGroupConfigs.stream() + private Rule parseModelGroupConfigs(List modelGroupConfigs) { + final List>> weightedRules = modelGroupConfigs.stream() .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } - private Rule parseModelGroupConfig(ModelGroupConfig config) { - final Rule matchingRule = parseMatchingRule(config); - final Rule defaultRule = parseDefaultActionRule(config); + private Rule parseModelGroupConfig(ModelGroupConfig config) { + final Rule matchingRule = parseMatchingRule(config); + final Rule defaultRule = parseDefaultActionRule(config); return combineRules(matchingRule, defaultRule); } - private Rule parseMatchingRule(ModelGroupConfig config) { + private Rule parseMatchingRule(ModelGroupConfig config) { final List schemaConfig = config.getSchema(); final List rulesConfig = config.getRules(); @@ -83,10 +83,10 @@ private Rule parseMatchingRule(ModelGroupConfig config) { final Schema schema = parseSchema(schemaConfig); - final List> rules = rulesConfig.stream() + final List> rules = rulesConfig.stream() .map(this::parseRuleConfig) .toList(); - final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); + final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); if (schemaConfig.size() != ruleTree.getDepth()) { throw new InvalidMatcherConfiguration("Schema functions count and rules matchers count mismatch"); @@ -109,15 +109,15 @@ private Schema parseSchema(List schema) { return Schema.of(schemaFunctions); } - private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { + private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); - final List> actions = parseActions(ruleConfig.getResults()); + final List> actions = parseActions(ruleConfig.getResults()); return RuleConfig.of(ruleFired, actions); } - private Rule parseDefaultActionRule(ModelGroupConfig config) { - final List defaultActionConfig = config.getDefaultAction(); + private Rule parseDefaultActionRule(ModelGroupConfig config) { + final List defaultActionConfig = config.getDefaultAction(); if (CollectionUtils.isEmpty(config.getDefaultAction())) { return null; @@ -127,19 +127,21 @@ private Rule parseDefaultActionRule(ModelGroupConfig config) { parseActions(defaultActionConfig), config.getAnalyticsKey(), config.getVersion()); } - private List> parseActions(List functionConfigs) { - final List> actions = functionConfigs.stream() + private List> parseActions(List functionConfigs) { + final List> actions = functionConfigs.stream() .map(config -> RuleAction.of( - specification.resultFunctionByName(config.getFunction()), config.getArgs())) + specification.resultFunctionByName(config.getFunction()), config.getConfig())) .toList(); actions.forEach(action -> - action.getFunction().validateConfigArguments(action.getConfigArguments())); + action.getFunction().validateConfig(action.getConfig())); return actions; } - private Rule combineRules(Rule left, Rule right) { + private Rule combineRules( + Rule left, Rule right) { + if (left == null && right == null) { return new NoOpRule<>(); } else if (left != null && right != null) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java index da77ada6e71..6feb59c8e66 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java @@ -9,5 +9,5 @@ public class AccountRuleConfig { List conditions; - List results; + List results; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index 9dea74f9259..7b304385eee 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -18,7 +18,7 @@ public class ModelGroupConfig { List schema; @JsonProperty("default") - List defaultAction; + List defaultAction; List rules; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java similarity index 70% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java index 158411a8127..acd9fa6d99b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java @@ -3,12 +3,10 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") -public class RuleFunctionConfig { +public class ResultFunctionConfig { String function; - List args; + JsonNode config; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java similarity index 73% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java index 069002a74e7..f2d343523e8 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestContext.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java @@ -1,4 +1,4 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; +package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; import lombok.Value; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java similarity index 72% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index 7077c7dec03..dc0884609f5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -1,8 +1,9 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; +package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -21,20 +22,20 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -public class RequestMatchingRule implements Rule { +public class RequestMatchingRule implements Rule { private static final String NULL_MATCHER = "null"; private final Schema schema; private final Set schemaFunctionNames; - private final RuleTree> ruleTree; + private final RuleTree> ruleTree; private final String modelVersion; private final String analyticsKey; private final String datacenter; public RequestMatchingRule(Schema schema, - RuleTree> ruleTree, + RuleTree> ruleTree, String modelVersion, String analyticsKey, String datacenter) { @@ -51,27 +52,27 @@ public RequestMatchingRule(Schema schema, } @Override - public RuleResult process(BidRequest bidRequest) { + public RuleResult process(BidRequest bidRequest, AuctionContext context) { return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() - ? processRule(bidRequest, null) - : processPerImpRule(bidRequest); + ? processRule(bidRequest, null, context) + : processPerImpRule(bidRequest, context); } - private RuleResult processPerImpRule(BidRequest bidRequest) { + private RuleResult processPerImpRule(BidRequest bidRequest, AuctionContext context) { return bidRequest.getImp().stream().reduce( RuleResult.unaltered(bidRequest), (result, imp) -> - result.mergeWith(processRule(result.getUpdateResult().getValue(), imp.getId())), + result.mergeWith(processRule(result.getUpdateResult().getValue(), imp.getId(), context)), RuleResult::mergeWith); } - private RuleResult processRule(BidRequest bidRequest, String impId) { - final RequestContext context = RequestContext.of(bidRequest, impId, datacenter); + private RuleResult processRule(BidRequest bidRequest, String impId, AuctionContext auctionContext) { + final RequestContext requestContext = RequestContext.of(bidRequest, impId, datacenter); final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(context, holder.getArguments()))) + SchemaFunctionArguments.of(requestContext, holder.getArguments()))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, NULL_MATCHER)) .toList(); @@ -80,7 +81,7 @@ private RuleResult processRule(BidRequest bidRequest, String impId) .collect(Collectors.toMap( idx -> schemaFunctions.get(idx).getName(), matchers::get, (left, right) -> left)); - final RuleConfig ruleConfig = ruleTree.getValue(matchers); + final RuleConfig ruleConfig = ruleTree.getValue(matchers); return ruleConfig.getActions().stream().reduce( RuleResult.unaltered(bidRequest), @@ -88,21 +89,23 @@ private RuleResult processRule(BidRequest bidRequest, String impId) applyAction( action, result.getUpdateResult().getValue(), + auctionContext, schemaFunctionResults, ruleConfig.getCondition())), RuleResult::mergeWith); } - private RuleResult applyAction(RuleAction action, + private RuleResult applyAction(RuleAction action, BidRequest bidRequest, + AuctionContext context, Map schemaFunctionResults, String condition) { - final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( - schemaFunctionResults, analyticsKey, condition, modelVersion); + final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( + context, schemaFunctionResults, analyticsKey, condition, modelVersion); - final ResultFunctionArguments arguments = ResultFunctionArguments.of( - bidRequest, action.getConfigArguments(), infrastructureArguments); + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + bidRequest, action.getConfig(), infrastructureArguments); return action.getFunction().apply(arguments); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java similarity index 56% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 887d4a2d768..0333bbd0727 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -1,29 +1,37 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request; +package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.RequestSchemaFunctions; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions.RequestSchemaFunctions; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.json.ObjectMapperProvider; import java.util.Map; import java.util.Set; @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class RequestSpecification implements StageSpecification { +public class RequestSpecification implements StageSpecification { public static final RequestSpecification INSTANCE = new RequestSpecification(); - public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(); + public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME); private static final Map> SCHEMA_FUNCTIONS = Map.of( RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION, - RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION); + RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION, + AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); - private static final Map> RESULT_FUNCTIONS = Map.of(); + private static final Map> RESULT_FUNCTIONS = Map.of( + "excludeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), + "includeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()) + ); public SchemaFunction schemaFunctionByName(String name) { final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); @@ -34,8 +42,8 @@ public SchemaFunction schemaFunctionByName(String name) { return function; } - public ResultFunction resultFunctionByName(String name) { - final ResultFunction function = RESULT_FUNCTIONS.get(name); + public ResultFunction resultFunctionByName(String name) { + final ResultFunction function = RESULT_FUNCTIONS.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java new file mode 100644 index 00000000000..49e2b7cbfef --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java @@ -0,0 +1,44 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.request.BidRequest; +import lombok.Value; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.springframework.util.CollectionUtils; + +@Value(staticConstructor = "of") +public class ExcludeBiddersFunction implements ResultFunction { + + ObjectMapper mapper; + + @Override + public RuleResult apply(ResultFunctionArguments arguments) { + return RuleResult.unaltered(arguments.getOperand()); + } + + @Override + public void validateConfig(JsonNode config) { + final ExcludeBiddersFunctionConfig parsedConfig = parseConfig(config); + if (parsedConfig == null) { + throw new ConfigurationValidationException("Configuration is required for excludeBidders function"); + } + + if (CollectionUtils.isEmpty(parsedConfig.getBidders())) { + throw new ConfigurationValidationException("bidders field is required for excludeBidders function"); + } + } + + private ExcludeBiddersFunctionConfig parseConfig(JsonNode config) { + try { + return mapper.treeToValue(config, ExcludeBiddersFunctionConfig.class); + } catch (JsonProcessingException e) { + throw new ConfigurationValidationException(e.getMessage()); + } + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java new file mode 100644 index 00000000000..8c14a64f92c --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java @@ -0,0 +1,20 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; + +import lombok.Builder; +import lombok.Value; + +import java.util.List; + +@Builder +@Value(staticConstructor = "of") +public class ExcludeBiddersFunctionConfig { + + List bidders; + + @Builder.Default + Integer seatNonBid = 203; + + Boolean ifSyncedId; + + String analyticsValue; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java new file mode 100644 index 00000000000..e7facb6d3e3 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -0,0 +1,73 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.List; +import java.util.Optional; + +public class AdUnitCodeFunction implements SchemaFunction { + + public static final String NAME = "adUnitCode"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final RequestContext context = arguments.getOperand(); + final String impId = context.getImpId(); + final BidRequest bidRequest = context.getBidRequest(); + + final Optional adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() + .filter(imp -> StringUtils.equals(impId, impId)) + .findFirst(); + + if (adUnit.isEmpty()) { + return UNDEFINED_RESULT; + } + + return adUnit.flatMap(AdUnitCodeFunction::extractGpid) + .or(() -> adUnit.map(AdUnitCodeFunction::extractTagId)) + .or(() -> adUnit.flatMap(AdUnitCodeFunction::extractPbAdSlot)) + .or(() -> adUnit.flatMap(AdUnitCodeFunction::extractStoredRequestId)) + .orElse(UNDEFINED_RESULT); + } + + private static Optional extractGpid(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("gpid")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } + + private static String extractTagId(Imp imp) { + return StringUtils.trimToNull(imp.getTagid()); + } + + private static Optional extractPbAdSlot(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("data")) + .map(data -> data.get("pbadslot")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } + + private static Optional extractStoredRequestId(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("prebid")) + .map(prebid -> prebid.get("storedrequest")) + .map(storedRequest -> storedRequest.get("id")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } + + @Override + public void validateConfigArguments(List configArguments) { + ValidationUtils.assertNoArgs(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java new file mode 100644 index 00000000000..29ef6b44de7 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java @@ -0,0 +1,21 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +import java.util.List; + +public class EidInFunction implements SchemaFunction { + + @Override + public String extract(SchemaFunctionArguments arguments) { + return ""; + } + + @Override + public void validateConfigArguments(List configArguments) { + SchemaFunction.super.validateConfigArguments(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java similarity index 87% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java index b271a96974a..2020f58edca 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/request/schema/functions/RequestSchemaFunctions.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java @@ -1,4 +1,4 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.request.schema.functions; +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; @@ -6,7 +6,7 @@ import com.iab.openrtb.request.User; import lombok.experimental.UtilityClass; import org.apache.commons.lang3.ObjectUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.AvailableFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.ExtractingFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.InFunction; @@ -27,7 +27,9 @@ public class RequestSchemaFunctions { public static final String DATACENTER_FUNCTION_NAME = "datacenter"; public static final String DATACENTER_IN_FUNCTION_NAME = "datacenterIn"; public static final String CHANNEL_FUNCTION_NAME = "channel"; + public static final String EID_IN_FUNCTION_NAME = "eidIn"; public static final String EID_AVAILABLE_FUNCTION_NAME = "eidAvailable"; + public static final String AD_UNIT_CODE_FUNCTION_NAME = "adUnitCode"; public static final SchemaFunction DEVICE_COUNTRY_FUNCTION = ExtractingFunction.of( RequestSchemaFunctions::extractDeviceCountry); @@ -46,11 +48,11 @@ private static String extractDeviceCountry(RequestContext context) { return Optional.ofNullable(context.getBidRequest().getDevice()) .map(Device::getGeo) .map(Geo::getCountry) - .orElse(SchemaFunction.NULL_RESULT); + .orElse(SchemaFunction.UNDEFINED_RESULT); } private static String extractDataCenter(RequestContext context) { - return ObjectUtils.defaultIfNull(context.getDatacenter(), SchemaFunction.NULL_RESULT); + return ObjectUtils.defaultIfNull(context.getDatacenter(), SchemaFunction.UNDEFINED_RESULT); } private static String extractChannel(RequestContext context) { @@ -59,7 +61,7 @@ private static String extractChannel(RequestContext context) { .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getChannel) .map(ExtRequestPrebidChannel::getName) - .orElse(SchemaFunction.NULL_RESULT); + .orElse(SchemaFunction.UNDEFINED_RESULT); } private static boolean isEidAvailable(RequestContext context) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java index 2ce70c0a127..365ef98e6af 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java @@ -2,21 +2,21 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; -public class AlternativeActionRule implements Rule { +public class AlternativeActionRule implements Rule { - private final Rule delegate; - private final Rule alternative; + private final Rule delegate; + private final Rule alternative; - public AlternativeActionRule(Rule delegate, Rule alternative) { + public AlternativeActionRule(Rule delegate, Rule alternative) { this.delegate = delegate; this.alternative = alternative; } - public RuleResult process(T value) { + public RuleResult process(T value, C context) { try { - return delegate.process(value); + return delegate.process(value, context); } catch (NoMatchingRuleException e) { - return alternative.process(value); + return alternative.process(value, context); } } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java index 08eed584ee6..100afc53c0d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java @@ -5,15 +5,15 @@ import java.util.List; @Value(staticConstructor = "of") -public class CompositeRule implements Rule { +public class CompositeRule implements Rule { - List> subrules; + List> subrules; @Override - public RuleResult process(T value) { + public RuleResult process(T value, C context) { return subrules.stream().reduce( RuleResult.unaltered(value), - (result, rule) -> result.mergeWith(rule.process(result.getUpdateResult().getValue())), + (result, rule) -> result.mergeWith(rule.process(result.getUpdateResult().getValue(), context)), RuleResult::mergeWith); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index 50382d4733e..4dcadde9622 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -7,34 +7,41 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; -public class DefaultActionRule implements Rule { +public class DefaultActionRule implements Rule { private static final String RULE_NAME = "default"; - private final List> actions; + private final List> actions; - private final InfrastructureArguments infrastructureArguments; + private final String analyticsKey; + private final String modelVersion; - public DefaultActionRule(List> actions, String analyticsKey, String modelVersion) { + public DefaultActionRule(List> actions, String analyticsKey, String modelVersion) { this.actions = ListUtils.emptyIfNull(actions); - infrastructureArguments = InfrastructureArguments.of( - Collections.emptyMap(), analyticsKey, RULE_NAME, modelVersion); + this.analyticsKey = Objects.requireNonNull(analyticsKey); + this.modelVersion = Objects.requireNonNull(modelVersion); } @Override - public RuleResult process(T value) { + public RuleResult process(T value, C context) { return actions.stream().reduce( RuleResult.unaltered(value), - (result, action) -> result.mergeWith(applyAction(action, result.getUpdateResult().getValue())), + (result, action) -> + result.mergeWith(applyAction(action, result.getUpdateResult().getValue(), context)), RuleResult::mergeWith); } - private RuleResult applyAction(RuleAction action, T value) { - final ResultFunctionArguments arguments = ResultFunctionArguments.of( - value, action.getConfigArguments(), infrastructureArguments); + private RuleResult applyAction(RuleAction action, T value, C context) { + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + value, action.getConfig(), infrastructureArguments(context)); return action.getFunction().apply(arguments); } + + private InfrastructureArguments infrastructureArguments(C context) { + return InfrastructureArguments.of(context, Collections.emptyMap(), analyticsKey, RULE_NAME, modelVersion); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java index bc1c037c955..66bfa2f0339 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java @@ -3,10 +3,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -public interface MatchingRuleFactory { +public interface MatchingRuleFactory { - Rule create(Schema schema, - RuleTree> ruleTree, - String analyticsKey, - String modelVersion); + Rule create(Schema schema, + RuleTree> ruleTree, + String analyticsKey, + String modelVersion); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java index e88dbe137c6..fa5cca636d5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -1,16 +1,17 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.NoArgsConstructor; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.model.UpdateResult; import java.util.Collections; @NoArgsConstructor -public class NoOpRule implements Rule { +public class NoOpRule implements Rule { @Override - public RuleResult process(T value) { + public RuleResult process(T value, C context) { return RuleResult.of(UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList())); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index b4df69e44e2..d15b90bce0e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -4,12 +4,13 @@ import lombok.Builder; import lombok.Value; import lombok.experimental.Accessors; +import org.prebid.server.auction.model.AuctionContext; @Builder @Accessors(fluent = true) @Value(staticConstructor = "of") public class PerStageRule { - Rule processedAuctionRequestRule; + Rule processedAuctionRequestRule; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java index b14bb23f4fc..1b369d52d9b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; -public interface Rule { +public interface Rule { - RuleResult process(T value); + RuleResult process(T value, C context); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java index ab8b28c9aec..fd361409018 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java @@ -6,9 +6,9 @@ import java.util.List; @Value(staticConstructor = "of") -public class RuleConfig { +public class RuleConfig { String condition; - List> actions; + List> actions; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java index fb4681b1dd5..d353a79a828 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java @@ -3,9 +3,9 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -public interface StageSpecification { +public interface StageSpecification { SchemaFunction schemaFunctionByName(String name); - ResultFunction resultFunctionByName(String name); + ResultFunction resultFunctionByName(String name); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java index 74d9cba3efe..e2d3c30dc22 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java @@ -6,20 +6,20 @@ import java.util.Objects; import java.util.random.RandomGenerator; -public class WeightedRule implements Rule { +public class WeightedRule implements Rule { private final RandomGenerator random; - private final WeightedList> weightedList; + private final WeightedList> weightedList; - public WeightedRule(RandomGenerator random, WeightedList> weightedList) { + public WeightedRule(RandomGenerator random, WeightedList> weightedList) { this.random = Objects.requireNonNull(random); this.weightedList = Objects.requireNonNull(weightedList); } @Override - public RuleResult process(T value) { + public RuleResult process(T value, C context) { try { - return weightedList.getForSeed(random.nextDouble()).process(value); + return weightedList.getForSeed(random.nextDouble()).process(value, context); } catch (NoMatchingValueException e) { return RuleResult.unaltered(value); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 49ef2fbc4d8..725465ed456 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -1,11 +1,14 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; import lombok.Value; +import org.prebid.server.auction.model.AuctionContext; import java.util.Map; @Value(staticConstructor = "of") -public class InfrastructureArguments { +public class InfrastructureArguments { + + C context; Map schemaFunctionResults; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index bc168dbe45b..092eb182128 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -3,11 +3,9 @@ import com.fasterxml.jackson.databind.JsonNode; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import java.util.List; +public interface ResultFunction { -public interface ResultFunction { + RuleResult apply(ResultFunctionArguments arguments); - RuleResult apply(ResultFunctionArguments arguments); - - void validateConfigArguments(List configArguments); + void validateConfig(JsonNode config); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java index d8d58e5a7cb..a58a980e49e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java @@ -3,14 +3,12 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") -public class ResultFunctionArguments { +public class ResultFunctionArguments { T operand; - List configArguments; + JsonNode config; - InfrastructureArguments infrastructureArguments; + InfrastructureArguments infrastructureArguments; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java index 09e20fc6d57..bddefb1e20a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java @@ -3,12 +3,10 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") -public class RuleAction { +public class RuleAction { - ResultFunction function; + ResultFunction function; - List configArguments; + JsonNode config; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 89e92a14065..28475945cd7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -6,7 +6,7 @@ public interface SchemaFunction { - String NULL_RESULT = "null"; + String UNDEFINED_RESULT = "undefined"; String extract(SchemaFunctionArguments arguments); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 3f794380cae..0e6406f9b7c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -10,8 +10,8 @@ public class RuleTreeFactory { - public static RuleTree> buildTree(List> rules) { - final List>> parsingContexts = toParsingContexts(rules); + public static RuleTree> buildTree(List> rules) { + final List>> parsingContexts = toParsingContexts(rules); final int depth = getDepth(parsingContexts); if (!parsingContexts.stream().allMatch(context -> context.argumentMatchers().size() == depth)) { @@ -21,7 +21,7 @@ public static RuleTree> buildTree(List> rules) { return new RuleTree<>(parseRuleNode(parsingContexts), depth); } - private static List>> toParsingContexts(List> rules) { + private static List>> toParsingContexts(List> rules) { return rules.stream() .map(rule -> new ParsingContext<>( List.of(StringUtils.defaultString(rule.getCondition()).split("\\|")), diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 35367e2b13f..2c83dde6cf3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -2,6 +2,7 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; @@ -34,17 +35,21 @@ public RuleEngineProcessedAuctionRequestHook(RuleRegistry ruleRegistry) { public Future> call(AuctionRequestPayload auctionRequestPayload, AuctionInvocationContext invocationContext) { + final AuctionContext context = invocationContext.auctionContext(); final String accountId = invocationContext.auctionContext().getAccount().getId(); return ruleRegistry.forAccount(accountId, invocationContext.accountConfig()) .map(PerStageRule::processedAuctionRequestRule) - .map(rule -> executeSafely(rule, auctionRequestPayload.bidRequest())) + .map(rule -> executeSafely(rule, auctionRequestPayload.bidRequest(), context)) .map(RuleEngineProcessedAuctionRequestHook::succeeded) .recover(RuleEngineProcessedAuctionRequestHook::failure); } - private static RuleResult executeSafely(Rule rule, BidRequest bidRequest) { + private static RuleResult executeSafely(Rule rule, + BidRequest bidRequest, + AuctionContext context) { + return rule != null - ? rule.process(bidRequest) + ? rule.process(bidRequest, context) : RuleResult.of(UpdateResult.unaltered(bidRequest), TagsImpl.of(Collections.emptyList())); } diff --git a/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java index 34f258356a7..5f489159f4c 100644 --- a/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java +++ b/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java @@ -35,7 +35,7 @@ public void processShouldAccumulateResultFromAllRuleActions() { given(firstFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("firstAction")); given(firstAction.getFunction()).willReturn(firstFunction); - given(firstAction.getConfigArguments()).willReturn(emptyList()); + given(firstAction.getConfig()).willReturn(emptyList()); final RuleAction secondAction = (RuleAction) mock(RuleAction.class); @@ -43,7 +43,7 @@ public void processShouldAccumulateResultFromAllRuleActions() { given(secondFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("secondAction")); given(secondAction.getFunction()).willReturn(secondFunction); - given(secondAction.getConfigArguments()).willReturn(emptyList()); + given(secondAction.getConfig()).willReturn(emptyList()); final DefaultActionRule target = new DefaultActionRule<>( asList(firstAction, secondAction), "analyticsKey", "modelVersion"); From f4bf5c9691bdfb68b401703d5da99f4e304c26f4 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 29 May 2025 19:02:27 +0300 Subject: [PATCH 29/94] WIP. --- .../core/request/RequestSpecification.java | 16 +++-- .../schema/functions/AdUnitCodeFunction.java | 29 +++----- .../schema/functions/ChannelFunction.java | 31 +++++++++ .../schema/functions/DataCenterFunction.java | 16 +++++ .../functions/DeviceCountryFunction.java | 24 +++++++ .../functions/DeviceCountryInFunction.java | 21 ++++++ .../schema/functions/MediaTypeInFunction.java | 69 +++++++++++++++++++ .../functions/RequestSchemaFunctions.java | 20 +----- .../core/rules/schema/SchemaFunction.java | 6 +- .../rules/schema/SchemaFunctionArguments.java | 6 +- 10 files changed, 188 insertions(+), 50 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 0333bbd0727..d2b2bc4b22c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -6,7 +6,11 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; -import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.RequestSchemaFunctions; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.ChannelFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DataCenterFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceCountryFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceCountryInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.MediaTypeInFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; @@ -24,14 +28,16 @@ public class RequestSpecification implements StageSpecification PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME); private static final Map> SCHEMA_FUNCTIONS = Map.of( - RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_FUNCTION, - RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION_NAME, RequestSchemaFunctions.DEVICE_COUNTRY_IN_FUNCTION, + DeviceCountryFunction.NAME, new DeviceCountryFunction(), + DeviceCountryInFunction.NAME, new DeviceCountryInFunction(), + MediaTypeInFunction.NAME, new MediaTypeInFunction(), + DataCenterFunction.NAME, new DataCenterFunction(), + ChannelFunction.NAME, new ChannelFunction(), AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); private static final Map> RESULT_FUNCTIONS = Map.of( "excludeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), - "includeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()) - ); + "includeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper())); public SchemaFunction schemaFunctionByName(String name) { final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index e7facb6d3e3..b1248579dc7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -8,9 +8,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -import java.util.List; import java.util.Optional; public class AdUnitCodeFunction implements SchemaFunction { @@ -23,18 +21,17 @@ public String extract(SchemaFunctionArguments arguments) { final String impId = context.getImpId(); final BidRequest bidRequest = context.getBidRequest(); - final Optional adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() + final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() .filter(imp -> StringUtils.equals(impId, impId)) - .findFirst(); + .findFirst() + .orElseThrow(() -> new IllegalStateException( + "Critical error in rules engine. Imp id of absent imp supplied")); - if (adUnit.isEmpty()) { - return UNDEFINED_RESULT; - } - return adUnit.flatMap(AdUnitCodeFunction::extractGpid) - .or(() -> adUnit.map(AdUnitCodeFunction::extractTagId)) - .or(() -> adUnit.flatMap(AdUnitCodeFunction::extractPbAdSlot)) - .or(() -> adUnit.flatMap(AdUnitCodeFunction::extractStoredRequestId)) + return extractGpid(adUnit) + .or(() -> extractTagId(adUnit)) + .or(() -> extractPbAdSlot(adUnit)) + .or(() -> extractStoredRequestId(adUnit)) .orElse(UNDEFINED_RESULT); } @@ -45,8 +42,9 @@ private static Optional extractGpid(Imp imp) { .map(JsonNode::asText); } - private static String extractTagId(Imp imp) { - return StringUtils.trimToNull(imp.getTagid()); + private static Optional extractTagId(Imp imp) { + return Optional.ofNullable(imp.getTagid()) + .filter(StringUtils::isNotBlank); } private static Optional extractPbAdSlot(Imp imp) { @@ -65,9 +63,4 @@ private static Optional extractStoredRequestId(Imp imp) { .filter(JsonNode::isTextual) .map(JsonNode::asText); } - - @Override - public void validateConfigArguments(List configArguments) { - ValidationUtils.assertNoArgs(configArguments); - } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java new file mode 100644 index 00000000000..55adaa82daf --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java @@ -0,0 +1,31 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; + +import java.util.Optional; + +public class ChannelFunction implements SchemaFunction { + + public static final String NAME = "channel"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getExt) + .map(ExtRequest::getPrebid) + .map(ExtRequestPrebid::getChannel) + .map(ExtRequestPrebidChannel::getName) + .map(ChannelFunction::resolveChannel) + .orElse(SchemaFunction.UNDEFINED_RESULT); + } + + private static String resolveChannel(String channel) { + return channel.equals("pbjs") ? "web" : channel; + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java new file mode 100644 index 00000000000..53c9a44a161 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java @@ -0,0 +1,16 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +public class DataCenterFunction implements SchemaFunction { + + public static final String NAME = "datacenter"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java new file mode 100644 index 00000000000..fd95e12811f --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java @@ -0,0 +1,24 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +import java.util.Optional; + +public class DeviceCountryFunction implements SchemaFunction { + + public static final String NAME = "deviceCountry"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getDevice) + .map(Device::getGeo) + .map(Geo::getCountry) + .orElse(UNDEFINED_RESULT); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java new file mode 100644 index 00000000000..44e4894a8cc --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java @@ -0,0 +1,21 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; + +public class DeviceCountryInFunction implements SchemaFunction { + + public static final String NAME = "deviceCountryIn"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return ""; + } + + @Override + public void validateConfigArguments(ObjectNode configArguments) { + SchemaFunction.super.validateConfigArguments(configArguments); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java new file mode 100644 index 00000000000..86dec757254 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -0,0 +1,69 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.SetUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.spring.config.bidder.model.MediaType; + +import java.util.HashSet; +import java.util.Set; + +public class MediaTypeInFunction implements SchemaFunction { + + public static final String NAME = "mediaTypeIn"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final RequestContext context = arguments.getOperand(); + + final String impId = context.getImpId(); + final BidRequest bidRequest = context.getBidRequest(); + + final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() + .filter(imp -> StringUtils.equals(impId, impId)) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + "Critical error in rules engine. Imp id of absent imp supplied")); + + final Set mediaTypesToMatch = parseMediaTypes(arguments.getConfig()); + final Set adUnitMediaTypes = adUnitMediaTypes(adUnit); + + boolean intersects = !SetUtils.intersection(mediaTypesToMatch, adUnitMediaTypes).isEmpty(); + + return Boolean.toString(intersects); + } + + private Set parseMediaTypes(ObjectNode config) { + // to lower case + } + + private static Set adUnitMediaTypes(Imp imp) { + final Set result = new HashSet<>(); + + if (imp.getBanner() != null) { + result.add(MediaType.BANNER.getKey()); + } + if (imp.getVideo() != null) { + result.add(MediaType.VIDEO.getKey()); + } + if (imp.getAudio() != null) { + result.add(MediaType.AUDIO.getKey()); + } + if (imp.getXNative() != null) { + result.add(MediaType.NATIVE.getKey()); + } + + return result; + } + + @Override + public void validateConfigArguments(ObjectNode args) { + SchemaFunction.super.validateConfigArguments(args); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java index 2020f58edca..205300a2a7d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java @@ -22,26 +22,17 @@ @UtilityClass public class RequestSchemaFunctions { - public static final String DEVICE_COUNTRY_FUNCTION_NAME = "deviceCountry"; public static final String DEVICE_COUNTRY_IN_FUNCTION_NAME = "deviceCountryIn"; - public static final String DATACENTER_FUNCTION_NAME = "datacenter"; public static final String DATACENTER_IN_FUNCTION_NAME = "datacenterIn"; - public static final String CHANNEL_FUNCTION_NAME = "channel"; public static final String EID_IN_FUNCTION_NAME = "eidIn"; public static final String EID_AVAILABLE_FUNCTION_NAME = "eidAvailable"; public static final String AD_UNIT_CODE_FUNCTION_NAME = "adUnitCode"; - public static final SchemaFunction DEVICE_COUNTRY_FUNCTION = ExtractingFunction.of( - RequestSchemaFunctions::extractDeviceCountry); public static final SchemaFunction DEVICE_COUNTRY_IN_FUNCTION = InFunction.of( RequestSchemaFunctions::extractDeviceCountry); - public static final SchemaFunction DATACENTER_FUNCTION = ExtractingFunction.of( - RequestSchemaFunctions::extractDataCenter); public static final SchemaFunction DATACENTER_IN_FUNCTION = InFunction.of( RequestSchemaFunctions::extractDataCenter); - public static final SchemaFunction CHANNEL_FUNCTION = ExtractingFunction.of( - RequestSchemaFunctions::extractChannel); - public static final SchemaFunction EID_AVAILABLE_FUNCTION = AvailableFunction.of( + public static final SchemaFunction EID_AVAILABLE_FUNCTION = AvailableFunction.of( RequestSchemaFunctions::isEidAvailable); private static String extractDeviceCountry(RequestContext context) { @@ -55,15 +46,6 @@ private static String extractDataCenter(RequestContext context) { return ObjectUtils.defaultIfNull(context.getDatacenter(), SchemaFunction.UNDEFINED_RESULT); } - private static String extractChannel(RequestContext context) { - return Optional.of(context.getBidRequest()) - .map(BidRequest::getExt) - .map(ExtRequest::getPrebid) - .map(ExtRequestPrebid::getChannel) - .map(ExtRequestPrebidChannel::getName) - .orElse(SchemaFunction.UNDEFINED_RESULT); - } - private static boolean isEidAvailable(RequestContext context) { return Optional.of(context.getBidRequest()) .map(BidRequest::getUser) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 28475945cd7..15ba75c6e62 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -1,8 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; -import com.fasterxml.jackson.databind.JsonNode; - -import java.util.List; +import com.fasterxml.jackson.databind.node.ObjectNode; public interface SchemaFunction { @@ -10,6 +8,6 @@ public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); - default void validateConfigArguments(List configArguments) { + default void validateConfigArguments(ObjectNode args) { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java index 7095031d28c..122afba582c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -1,14 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") public class SchemaFunctionArguments { T operand; - List configArguments; + ObjectNode config; } From be508260a14061c56dd3419298c72db4a5f1624f Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 29 May 2025 19:07:31 +0300 Subject: [PATCH 30/94] Cleanup. --- .../engine/core/config/StageConfigParser.java | 2 +- .../config/model/SchemaFunctionConfig.java | 6 +- .../core/request/RequestMatchingRule.java | 2 +- .../core/request/RequestSpecification.java | 5 +- .../functions/ExcludeBiddersFunction.java | 2 + .../functions/IncludeBiddersFunction.java | 47 ++++++++++++++++ .../schema/functions/EidInFunction.java | 8 +-- .../schema/functions/MediaTypeInFunction.java | 2 + .../functions/RequestSchemaFunctions.java | 56 ------------------- .../core/rules/schema/AvailableFunction.java | 24 -------- .../core/rules/schema/ExtractingFunction.java | 24 -------- .../engine/core/rules/schema/InFunction.java | 28 ---------- .../rules/schema/SchemaFunctionHolder.java | 6 +- 13 files changed, 63 insertions(+), 149 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 96f9cfc6f83..5b7198b54e5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -104,7 +104,7 @@ private Schema parseSchema(List schema) { .toList(); schemaFunctions.forEach(holder -> - holder.getSchemaFunction().validateConfigArguments(holder.getArguments())); + holder.getSchemaFunction().validateConfigArguments(holder.getConfig())); return Schema.of(schemaFunctions); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java index c814b0510b6..264abef359a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java @@ -1,14 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.config.model; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") public class SchemaFunctionConfig { String function; - List args; + ObjectNode args; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index dc0884609f5..00ac4aa5e4a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -72,7 +72,7 @@ private RuleResult processRule(BidRequest bidRequest, String impId, final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(requestContext, holder.getArguments()))) + SchemaFunctionArguments.of(requestContext, holder.getConfig()))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, NULL_MATCHER)) .toList(); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index d2b2bc4b22c..3929470dd67 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -5,6 +5,7 @@ import lombok.NoArgsConstructor; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.ChannelFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DataCenterFunction; @@ -36,8 +37,8 @@ ChannelFunction.NAME, new ChannelFunction(), AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); private static final Map> RESULT_FUNCTIONS = Map.of( - "excludeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), - "includeBidders", ExcludeBiddersFunction.of(ObjectMapperProvider.mapper())); + ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), + IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(ObjectMapperProvider.mapper())); public SchemaFunction schemaFunctionByName(String name) { final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java index 49e2b7cbfef..fb96bf38c50 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java @@ -15,6 +15,8 @@ @Value(staticConstructor = "of") public class ExcludeBiddersFunction implements ResultFunction { + public static final String NAME = "excludeBidders"; + ObjectMapper mapper; @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java new file mode 100644 index 00000000000..4d5810a817b --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java @@ -0,0 +1,47 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.request.BidRequest; +import lombok.Value; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.springframework.util.CollectionUtils; + +@Value(staticConstructor = "of") +public class IncludeBiddersFunction implements ResultFunction { + + public static final String NAME = "includeBidders"; + + ObjectMapper mapper; + + @Override + public RuleResult apply(ResultFunctionArguments arguments) { + return RuleResult.unaltered(arguments.getOperand()); + } + + @Override + public void validateConfig(JsonNode config) { + final ExcludeBiddersFunctionConfig parsedConfig = parseConfig(config); + if (parsedConfig == null) { + throw new ConfigurationValidationException("Configuration is required for excludeBidders function"); + } + + if (CollectionUtils.isEmpty(parsedConfig.getBidders())) { + throw new ConfigurationValidationException("bidders field is required for excludeBidders function"); + } + } + + private ExcludeBiddersFunctionConfig parseConfig(JsonNode config) { + try { + return mapper.treeToValue(config, ExcludeBiddersFunctionConfig.class); + } catch (JsonProcessingException e) { + throw new ConfigurationValidationException(e.getMessage()); + } + } +} + diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java index 29ef6b44de7..748ccdfedb0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java @@ -1,12 +1,10 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; -import java.util.List; - public class EidInFunction implements SchemaFunction { @Override @@ -15,7 +13,7 @@ public String extract(SchemaFunctionArguments arguments) { } @Override - public void validateConfigArguments(List configArguments) { - SchemaFunction.super.validateConfigArguments(configArguments); + public void validateConfigArguments(ObjectNode config) { + SchemaFunction.super.validateConfigArguments(config); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index 86dec757254..b4fc97654a2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -11,6 +11,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.spring.config.bidder.model.MediaType; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -41,6 +42,7 @@ public String extract(SchemaFunctionArguments arguments) { private Set parseMediaTypes(ObjectNode config) { // to lower case + return Collections.emptySet(); } private static Set adUnitMediaTypes(Imp imp) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java deleted file mode 100644 index 205300a2a7d..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/RequestSchemaFunctions.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; - -import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Device; -import com.iab.openrtb.request.Geo; -import com.iab.openrtb.request.User; -import lombok.experimental.UtilityClass; -import org.apache.commons.lang3.ObjectUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.AvailableFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.ExtractingFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.InFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; - -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; - -@UtilityClass -public class RequestSchemaFunctions { - - public static final String DEVICE_COUNTRY_IN_FUNCTION_NAME = "deviceCountryIn"; - public static final String DATACENTER_IN_FUNCTION_NAME = "datacenterIn"; - public static final String EID_IN_FUNCTION_NAME = "eidIn"; - public static final String EID_AVAILABLE_FUNCTION_NAME = "eidAvailable"; - public static final String AD_UNIT_CODE_FUNCTION_NAME = "adUnitCode"; - - public static final SchemaFunction DEVICE_COUNTRY_IN_FUNCTION = InFunction.of( - RequestSchemaFunctions::extractDeviceCountry); - public static final SchemaFunction DATACENTER_IN_FUNCTION = InFunction.of( - RequestSchemaFunctions::extractDataCenter); - public static final SchemaFunction EID_AVAILABLE_FUNCTION = AvailableFunction.of( - RequestSchemaFunctions::isEidAvailable); - - private static String extractDeviceCountry(RequestContext context) { - return Optional.ofNullable(context.getBidRequest().getDevice()) - .map(Device::getGeo) - .map(Geo::getCountry) - .orElse(SchemaFunction.UNDEFINED_RESULT); - } - - private static String extractDataCenter(RequestContext context) { - return ObjectUtils.defaultIfNull(context.getDatacenter(), SchemaFunction.UNDEFINED_RESULT); - } - - private static boolean isEidAvailable(RequestContext context) { - return Optional.of(context.getBidRequest()) - .map(BidRequest::getUser) - .map(User::getEids) - .filter(Predicate.not(List::isEmpty)) - .isPresent(); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java deleted file mode 100644 index fcd009fda55..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/AvailableFunction.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; - -import java.util.List; -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class AvailableFunction implements SchemaFunction { - - Function isAvailable; - - @Override - public String extract(SchemaFunctionArguments arguments) { - return Boolean.toString(isAvailable.apply(arguments.getOperand())); - } - - @Override - public void validateConfigArguments(List configArguments) { - ValidationUtils.assertNoArgs(configArguments); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java deleted file mode 100644 index ab57ecd8acc..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/ExtractingFunction.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; - -import java.util.List; -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class ExtractingFunction implements SchemaFunction { - - Function extractor; - - @Override - public String extract(SchemaFunctionArguments arguments) { - return extractor.apply(arguments.getOperand()); - } - - @Override - public void validateConfigArguments(List configArguments) { - ValidationUtils.assertNoArgs(configArguments); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java deleted file mode 100644 index 99c481bb582..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/InFunction.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; - -import java.util.List; -import java.util.function.Function; - -@Value(staticConstructor = "of") -public class InFunction implements SchemaFunction { - - Function extractor; - - @Override - public String extract(SchemaFunctionArguments arguments) { - final String matchingValue = extractor.apply(arguments.getOperand()); - final boolean isValueInList = arguments.getConfigArguments().stream() - .anyMatch(argument -> argument.asText().equals(matchingValue)); - - return Boolean.toString(isValueInList); - } - - @Override - public void validateConfigArguments(List configArguments) { - ValidationUtils.assertArrayOfStrings(configArguments); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java index f074eb1659c..caaa317b47a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -1,10 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.schema; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; -import java.util.List; - @Value(staticConstructor = "of") public class SchemaFunctionHolder { @@ -12,5 +10,5 @@ public class SchemaFunctionHolder { SchemaFunction schemaFunction; - List arguments; + ObjectNode config; } From f2904d5d57fcaa23ba191246be5c034a7106a1ba Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 2 Jun 2025 17:22:43 +0300 Subject: [PATCH 31/94] Added a bunch of schema functions. --- .../engine/core/config/StageConfigParser.java | 13 ++- .../schema/functions/AdUnitCodeFunction.java | 9 +- .../functions/AdUnitCodeInFunction.java | 91 +++++++++++++++++++ .../schema/functions/BundleFunction.java | 27 ++++++ .../schema/functions/BundleInFunction.java | 37 ++++++++ .../schema/functions/ChannelFunction.java | 7 ++ .../schema/functions/DataCenterFunction.java | 7 ++ .../functions/DataCenterInFunction.java | 33 +++++++ .../functions/DeviceCountryFunction.java | 7 ++ .../functions/DeviceCountryInFunction.java | 25 ++++- .../schema/functions/DeviceTypeFunction.java | 28 ++++++ .../functions/DeviceTypeInFunction.java | 41 +++++++++ .../schema/functions/DomainFunction.java | 25 +++++ .../functions/EidAvailableFunction.java | 34 +++++++ .../schema/functions/EidInFunction.java | 33 ++++++- .../functions/FpdAvailableFunction.java | 90 ++++++++++++++++++ .../functions/GppSidAvailableFunction.java | 36 ++++++++ .../request/schema/functions/GppSidIn.java | 44 +++++++++ .../schema/functions/MediaTypeInFunction.java | 21 ++--- .../schema/functions/PrebidKeyFunction.java | 29 ++++++ .../schema/functions/TcfInScopeFunction.java | 32 +++++++ .../functions/UserFpdAvailableFunction.java | 41 +++++++++ .../core/rules/schema/SchemaFunction.java | 3 +- .../schema/functions/PercentFunction.java | 30 ++++++ .../engine/core/util/ValidationUtils.java | 60 ++++++++++-- 25 files changed, 773 insertions(+), 30 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 5b7198b54e5..e5f99902177 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -22,6 +22,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedEntry; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import org.springframework.util.CollectionUtils; @@ -103,12 +104,20 @@ private Schema parseSchema(List schema) { config.getArgs())) .toList(); - schemaFunctions.forEach(holder -> - holder.getSchemaFunction().validateConfigArguments(holder.getConfig())); + schemaFunctions.forEach(this::validateFunctionConfig); return Schema.of(schemaFunctions); } + private void validateFunctionConfig(SchemaFunctionHolder holder) { + try { + holder.getSchemaFunction().validateConfig(holder.getConfig()); + } catch (ConfigurationValidationException exception) { + throw new InvalidMatcherConfiguration( + "Function '%s' configuration is invalid: %s".formatted(holder.getName(), exception.getMessage())); + } + } + private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); final List> actions = parseActions(ruleConfig.getResults()); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index b1248579dc7..d9311cade9e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; @@ -8,6 +9,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; @@ -22,7 +24,7 @@ public String extract(SchemaFunctionArguments arguments) { final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() - .filter(imp -> StringUtils.equals(impId, impId)) + .filter(imp -> StringUtils.equals(imp.getId(), impId)) .findFirst() .orElseThrow(() -> new IllegalStateException( "Critical error in rules engine. Imp id of absent imp supplied")); @@ -63,4 +65,9 @@ private static Optional extractStoredRequestId(Imp imp) { .filter(JsonNode::isTextual) .map(JsonNode::asText); } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java new file mode 100644 index 00000000000..2957770077d --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -0,0 +1,91 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class AdUnitCodeInFunction implements SchemaFunction { + + public static final String NAME = "adUnitCodeIn"; + + private static final String CODES_FIELD = "codes"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final RequestContext context = arguments.getOperand(); + final String impId = context.getImpId(); + final BidRequest bidRequest = context.getBidRequest(); + + final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() + .filter(imp -> StringUtils.equals(imp.getId(), impId)) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + "Critical error in rules engine. Imp id of absent imp supplied")); + + final Set adUnitPotentialCodes = Stream.of( + extractGpid(adUnit), + extractTagId(adUnit), + extractPbAdSlot(adUnit), + extractStoredRequestId(adUnit)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(CODES_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(adUnitPotentialCodes::contains); + + return Boolean.toString(matches); + } + + private static String extractGpid(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("gpid")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText) + .orElse(null); + } + + private static String extractTagId(Imp imp) { + return Optional.ofNullable(imp.getTagid()) + .filter(StringUtils::isNotBlank) + .orElse(null); + } + + private static String extractPbAdSlot(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("data")) + .map(data -> data.get("pbadslot")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText) + .orElse(null); + } + + private static String extractStoredRequestId(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("prebid")) + .map(prebid -> prebid.get("storedrequest")) + .map(storedRequest -> storedRequest.get("id")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText) + .orElse(null); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, CODES_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java new file mode 100644 index 00000000000..b5407c1bdae --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java @@ -0,0 +1,27 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.Optional; + +public class BundleFunction implements SchemaFunction { + + public static final String NAME = "bundle"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) + .map(App::getBundle) + .orElse(UNDEFINED_RESULT); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java new file mode 100644 index 00000000000..fd35c184e49 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java @@ -0,0 +1,37 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Optional; + +public class BundleInFunction implements SchemaFunction { + + public static final String NAME = "bundleIn"; + + private static final String BUNDLES_FIELD = "bundles"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final String bundle = Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) + .map(App::getBundle) + .orElse(UNDEFINED_RESULT); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(BUNDLES_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(bundle::equals); + + return Boolean.toString(matches); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, BUNDLES_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java index 55adaa82daf..3f02a589f17 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java @@ -1,9 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; @@ -28,4 +30,9 @@ public String extract(SchemaFunctionArguments arguments) { private static String resolveChannel(String channel) { return channel.equals("pbjs") ? "web" : channel; } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java index 53c9a44a161..d9c69ebacf5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java @@ -1,9 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; public class DataCenterFunction implements SchemaFunction { @@ -13,4 +15,9 @@ public class DataCenterFunction implements SchemaFunction { public String extract(SchemaFunctionArguments arguments) { return StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java new file mode 100644 index 00000000000..e417a1da8b5 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java @@ -0,0 +1,33 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +public class DataCenterInFunction implements SchemaFunction { + + private static final String NAME = "datacenterIn"; + + private static final String DATACENTERS_FIELD = "datacenters"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final String datacenter = StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(DATACENTERS_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(datacenter::equalsIgnoreCase); + + return Boolean.toString(matches); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, DATACENTERS_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java index fd95e12811f..c0d35e70581 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java @@ -1,11 +1,13 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; @@ -21,4 +23,9 @@ public String extract(SchemaFunctionArguments arguments) { .map(Geo::getCountry) .orElse(UNDEFINED_RESULT); } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java index 44e4894a8cc..f4f6064327f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java @@ -1,21 +1,40 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Optional; public class DeviceCountryInFunction implements SchemaFunction { public static final String NAME = "deviceCountryIn"; + private static final String COUNTRIES_FIELD = "countries"; @Override public String extract(SchemaFunctionArguments arguments) { - return ""; + final String deviceCountry = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getDevice) + .map(Device::getGeo) + .map(Geo::getCountry) + .orElse(UNDEFINED_RESULT); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(COUNTRIES_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(deviceCountry::equalsIgnoreCase); + + return Boolean.toString(matches); } @Override - public void validateConfigArguments(ObjectNode configArguments) { - SchemaFunction.super.validateConfigArguments(configArguments); + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, COUNTRIES_FIELD); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java new file mode 100644 index 00000000000..973b33484ed --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java @@ -0,0 +1,28 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Device; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.Optional; + +public class DeviceTypeFunction implements SchemaFunction { + + public static final String NAME = "deviceType"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + return Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) + .map(Device::getDevicetype) + .map(String::valueOf) + .orElse(UNDEFINED_RESULT); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java new file mode 100644 index 00000000000..e7a3e20bd89 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java @@ -0,0 +1,41 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Device; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Optional; + +public class DeviceTypeInFunction implements SchemaFunction { + + public static final String NAME = "deviceTypeIn"; + + public static final String TYPES_FIELD = "types"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final Integer deviceType = Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) + .map(Device::getDevicetype) + .orElse(null); + + if (deviceType == null) { + return Boolean.FALSE.toString(); + } + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(TYPES_FIELD).elements()) + .map(JsonNode::asInt) + .anyMatch(deviceType::equals); + + return Boolean.toString(matches); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfIntegers(config, TYPES_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java new file mode 100644 index 00000000000..cc46b604563 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java @@ -0,0 +1,25 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +public class DomainFunction implements SchemaFunction { + + private static final String NAME = "domain"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + + return UNDEFINED_RESULT; + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java new file mode 100644 index 00000000000..e60f456b2d2 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java @@ -0,0 +1,34 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.User; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public class EidAvailableFunction implements SchemaFunction { + + public static final String NAME = "eidAvailable"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final boolean available = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getUser) + .map(User::getEids) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + + return Boolean.toString(available); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java index 748ccdfedb0..2783724df3c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java @@ -1,19 +1,46 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.User; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; public class EidInFunction implements SchemaFunction { + public static final String NAME = "eidIn"; + + private static final String SOURCES_FIELD = "sources"; + @Override public String extract(SchemaFunctionArguments arguments) { - return ""; + final Set sources = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getUser) + .map(User::getEids) + .stream() + .flatMap(Collection::stream) + .map(Eid::getSource) + .collect(Collectors.toSet()); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(SOURCES_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(sources::contains); + + return Boolean.toString(matches); } @Override - public void validateConfigArguments(ObjectNode config) { - SchemaFunction.super.validateConfigArguments(config); + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, SOURCES_FIELD); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java new file mode 100644 index 00000000000..a92524d80ac --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java @@ -0,0 +1,90 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Content; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.User; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.proto.openrtb.ext.request.ExtApp; +import org.prebid.server.proto.openrtb.ext.request.ExtSite; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public class FpdAvailableFunction implements SchemaFunction { + + public static final String NAME = "fpdAvailable"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + + final boolean available = isUserDataAvailable(bidRequest) + || isUserExtDataAvailable(bidRequest) + || isSiteContentDataAvailable(bidRequest) + || isSiteExtDataAvailable(bidRequest) + || isAppContentDataAvailable(bidRequest) + || isAppExtDataAvailable(bidRequest); + + return Boolean.toString(available); + } + + private static boolean isUserDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getUser()) + .map(User::getData) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + } + + private static boolean isUserExtDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getUser()) + .map(User::getExt) + .map(ExtUser::getData) + .filter(Predicate.not(ObjectNode::isEmpty)) + .isPresent(); + } + + private static boolean isSiteContentDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()) + .map(Site::getContent) + .map(Content::getData) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + } + + private static boolean isSiteExtDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()) + .map(Site::getExt) + .map(ExtSite::getData) + .filter(Predicate.not(ObjectNode::isEmpty)) + .isPresent(); + } + + private static boolean isAppContentDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getApp()) + .map(App::getContent) + .map(Content::getData) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + } + + private static boolean isAppExtDataAvailable(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getApp()) + .map(App::getExt) + .map(ExtApp::getData) + .filter(Predicate.not(ObjectNode::isEmpty)) + .isPresent(); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java new file mode 100644 index 00000000000..a183103f4d2 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java @@ -0,0 +1,36 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Regs; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; + +public class GppSidAvailableFunction implements SchemaFunction { + + public static final String NAME = "gppSidAvailable"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final boolean available = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getRegs) + .map(Regs::getGppSid) + .stream() + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .anyMatch(sid -> sid > 0); + + return Boolean.toString(available); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java new file mode 100644 index 00000000000..4304d6d13aa --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java @@ -0,0 +1,44 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Regs; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class GppSidIn implements SchemaFunction { + + public static final String NAME = "gppSidIn"; + + private static final String SIDS_FIELD = "sids"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final Set sids = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getRegs) + .map(Regs::getGppSid) + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(SIDS_FIELD).elements()) + .map(JsonNode::asInt) + .anyMatch(sids::contains); + + return Boolean.toString(matches); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfIntegers(config, SIDS_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index b4fc97654a2..f31baa3d9ec 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -1,17 +1,18 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; -import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.spring.config.bidder.model.MediaType; +import org.prebid.server.util.StreamUtil; -import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -19,6 +20,8 @@ public class MediaTypeInFunction implements SchemaFunction { public static final String NAME = "mediaTypeIn"; + private static final String TYPES_FIELD = "types"; + @Override public String extract(SchemaFunctionArguments arguments) { final RequestContext context = arguments.getOperand(); @@ -32,19 +35,15 @@ public String extract(SchemaFunctionArguments arguments) { .orElseThrow(() -> new IllegalStateException( "Critical error in rules engine. Imp id of absent imp supplied")); - final Set mediaTypesToMatch = parseMediaTypes(arguments.getConfig()); final Set adUnitMediaTypes = adUnitMediaTypes(adUnit); - boolean intersects = !SetUtils.intersection(mediaTypesToMatch, adUnitMediaTypes).isEmpty(); + boolean intersects = StreamUtil.asStream(arguments.getConfig().get(TYPES_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(adUnitMediaTypes::contains); return Boolean.toString(intersects); } - private Set parseMediaTypes(ObjectNode config) { - // to lower case - return Collections.emptySet(); - } - private static Set adUnitMediaTypes(Imp imp) { final Set result = new HashSet<>(); @@ -65,7 +64,7 @@ private static Set adUnitMediaTypes(Imp imp) { } @Override - public void validateConfigArguments(ObjectNode args) { - SchemaFunction.super.validateConfigArguments(args); + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, TYPES_FIELD); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java new file mode 100644 index 00000000000..84e8ebdb453 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java @@ -0,0 +1,29 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +public class PrebidKeyFunction implements SchemaFunction { + + public static final String NAME = "prebidKey"; + + private static final String KEY_FIELD = "key"; + + @Override + public String extract(SchemaFunctionArguments arguments) { +// final String key = arguments.getConfig().get(KEY_FIELD).asText(); +// return Optional.ofNullable(arguments.getOperand().getBidRequest().getExt()) +// .map(ext -> ext.getPrebid()) +// .map(e -> e.get) + + return UNDEFINED_RESULT; + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertString(config, KEY_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java new file mode 100644 index 00000000000..3f82e221d36 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java @@ -0,0 +1,32 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Regs; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.Optional; + +public class TcfInScopeFunction implements SchemaFunction { + + public static final String NAME = "tcfInScope"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final boolean inScope = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getRegs) + .map(Regs::getGdpr) + .filter(Integer.valueOf(1)::equals) + .isPresent(); + + return Boolean.toString(inScope); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java new file mode 100644 index 00000000000..3960388939f --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java @@ -0,0 +1,41 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.User; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public class UserFpdAvailableFunction implements SchemaFunction { + + public static final String NAME = "userFpdAvailable"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final Optional user = Optional.of(arguments.getOperand().getBidRequest()) + .map(BidRequest::getUser); + + final boolean userDataAvailable = user.map(User::getData) + .filter(Predicate.not(List::isEmpty)) + .isPresent(); + + final boolean userExtDataAvailable = user.map(User::getExt) + .map(ExtUser::getData) + .filter(Predicate.not(ObjectNode::isEmpty)) + .isPresent(); + + return Boolean.toString(userDataAvailable || userExtDataAvailable); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertNoArgs(config); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 15ba75c6e62..03641526a93 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -8,6 +8,5 @@ public interface SchemaFunction { String extract(SchemaFunctionArguments arguments); - default void validateConfigArguments(ObjectNode args) { - } + void validateConfig(ObjectNode config); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java new file mode 100644 index 00000000000..580e8941b31 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java @@ -0,0 +1,30 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.schema.functions; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; + +import java.util.random.RandomGenerator; + +@RequiredArgsConstructor +public class PercentFunction implements SchemaFunction { + + public static final String NAME = "percent"; + + private static final String PCT_FIELD = "pct"; + + private final RandomGenerator random; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final int resolvedUpperBound = Math.min(Math.max(arguments.getConfig().get(PCT_FIELD).asInt(), 0), 100); + return Boolean.toString(random.nextInt(100) < resolvedUpperBound); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertInteger(config, PCT_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java index 7356bd8a9f6..4b040c263b4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java @@ -1,21 +1,65 @@ package org.prebid.server.hooks.modules.rule.engine.core.util; import com.fasterxml.jackson.databind.JsonNode; -import org.springframework.util.CollectionUtils; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.util.StreamUtil; -import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; public class ValidationUtils { - public static void assertArrayOfStrings(List configurationArguments) { - if (CollectionUtils.isEmpty(configurationArguments) - || !configurationArguments.stream().allMatch(JsonNode::isTextual)) { - throw new ConfigurationValidationException("Array of strings required"); + public static void assertArrayOfStrings(ObjectNode config, String fieldName) { + assertArrayOf( + config, + fieldName, + JsonNode::isTextual, + "Field '%s' is required and has to be an array of strings"); + } + + public static void assertArrayOfIntegers(ObjectNode config, String fieldName) { + assertArrayOf( + config, + fieldName, + JsonNode::isInt, + "Field '%s' is required and has to be an array of integers"); + } + + private static void assertArrayOf(ObjectNode config, + String fieldName, + Predicate predicate, + String messageTemplate) { + + Optional.ofNullable(config) + .map(node -> node.get(fieldName)) + .filter(JsonNode::isArray) + .map(node -> (ArrayNode) node) + .filter(Predicate.not(ArrayNode::isEmpty)) + .filter(node -> StreamUtil.asStream(node.elements()).allMatch(predicate)) + .orElseThrow(() -> new ConfigurationValidationException(messageTemplate.formatted(fieldName))); + } + + public static void assertString(ObjectNode config, String fieldName) { + assertField(config, fieldName, JsonNode::isTextual, "Field '%s' is required and has to be a string"); + } + + public static void assertInteger(ObjectNode config, String fieldName) { + assertField(config, fieldName, JsonNode::isInt, "Field '%s' is required and has to be an integer"); + } + + private static void assertField(ObjectNode config, + String fieldName, + Predicate predicate, + String messageTemplate) { + + if (config == null || !config.has(fieldName) || !predicate.test(config.get(fieldName))) { + throw new ConfigurationValidationException(messageTemplate.formatted(fieldName)); } } - public static void assertNoArgs(List configurationArguments) { - if (configurationArguments != null) { + public static void assertNoArgs(ObjectNode config) { + if (config != null && !config.isEmpty()) { throw new ConfigurationValidationException("No arguments allowed"); } } From b91ceb802dcd236811c23aead9dff6a20d5e50aa Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 2 Jun 2025 17:47:36 +0300 Subject: [PATCH 32/94] Made compiler happy. --- .../config/RuleEngineModuleConfiguration.java | 2 +- .../core/request/RequestSpecification.java | 73 ++++++++++++++----- .../functions/DataCenterInFunction.java | 2 +- .../schema/functions/DomainFunction.java | 2 +- .../{GppSidIn.java => GppSidInFunction.java} | 2 +- 5 files changed, 60 insertions(+), 21 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/{GppSidIn.java => GppSidInFunction.java} (95%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 5743af1e761..0a5bb2b767f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -35,7 +35,7 @@ StageConfigParser processedAuctionRe return new StageConfigParser<>( () -> ThreadLocalRandom.current().nextLong(), Stage.processed_auction_request, - RequestSpecification.INSTANCE, + new RequestSpecification(ObjectMapperProvider.mapper()), (schema, ruleTree, modelVersion, analyticsKey) -> new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 3929470dd67..0bdada2b049 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -1,47 +1,86 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; +import com.fasterxml.jackson.databind.ObjectMapper; import com.iab.openrtb.request.BidRequest; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.BundleFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.BundleInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.ChannelFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DataCenterFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DataCenterInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceCountryFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceCountryInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceTypeFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceTypeInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DomainFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.EidAvailableFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.EidInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.FpdAvailableFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.GppSidAvailableFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.GppSidInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.MediaTypeInFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.PrebidKeyFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.TcfInScopeFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.UserFpdAvailableFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.json.ObjectMapperProvider; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; -@NoArgsConstructor(access = AccessLevel.PRIVATE) public class RequestSpecification implements StageSpecification { - public static final RequestSpecification INSTANCE = new RequestSpecification(); + public static final Set PER_IMP_SCHEMA_FUNCTIONS = + Set.of(AdUnitCodeFunction.NAME, MediaTypeInFunction.NAME); - public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME); + private final ObjectMapper mapper; - private static final Map> SCHEMA_FUNCTIONS = Map.of( - DeviceCountryFunction.NAME, new DeviceCountryFunction(), - DeviceCountryInFunction.NAME, new DeviceCountryInFunction(), - MediaTypeInFunction.NAME, new MediaTypeInFunction(), - DataCenterFunction.NAME, new DataCenterFunction(), - ChannelFunction.NAME, new ChannelFunction(), - AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); + private final Map> schemaFunctions; + private final Map> resultFunctions; + + public RequestSpecification(ObjectMapper mapper) { + this.mapper = Objects.requireNonNull(mapper); + + schemaFunctions = new HashMap<>(); + schemaFunctions.put(AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); + schemaFunctions.put(AdUnitCodeInFunction.NAME, new AdUnitCodeInFunction()); + schemaFunctions.put(BundleFunction.NAME, new BundleFunction()); + schemaFunctions.put(BundleInFunction.NAME, new BundleInFunction()); + schemaFunctions.put(ChannelFunction.NAME, new ChannelFunction()); + schemaFunctions.put(DataCenterFunction.NAME, new DataCenterFunction()); + schemaFunctions.put(DataCenterInFunction.NAME, new DataCenterInFunction()); + schemaFunctions.put(DeviceCountryFunction.NAME, new DeviceCountryFunction()); + schemaFunctions.put(DeviceCountryInFunction.NAME, new DeviceCountryInFunction()); + schemaFunctions.put(DeviceTypeFunction.NAME, new DeviceTypeFunction()); + schemaFunctions.put(DeviceTypeInFunction.NAME, new DeviceTypeInFunction()); + schemaFunctions.put(DomainFunction.NAME, new DomainFunction()); + schemaFunctions.put(EidAvailableFunction.NAME, new EidAvailableFunction()); + schemaFunctions.put(EidInFunction.NAME, new EidInFunction()); + schemaFunctions.put(FpdAvailableFunction.NAME, new FpdAvailableFunction()); + schemaFunctions.put(GppSidAvailableFunction.NAME, new GppSidAvailableFunction()); + schemaFunctions.put(GppSidInFunction.NAME, new GppSidInFunction()); + schemaFunctions.put(MediaTypeInFunction.NAME, new MediaTypeInFunction()); + schemaFunctions.put(PrebidKeyFunction.NAME, new PrebidKeyFunction()); + schemaFunctions.put(TcfInScopeFunction.NAME, new TcfInScopeFunction()); + schemaFunctions.put(UserFpdAvailableFunction.NAME, new UserFpdAvailableFunction()); + + resultFunctions = Map.of( + ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), + IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(ObjectMapperProvider.mapper())); + } - private static final Map> RESULT_FUNCTIONS = Map.of( - ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), - IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(ObjectMapperProvider.mapper())); public SchemaFunction schemaFunctionByName(String name) { - final SchemaFunction function = SCHEMA_FUNCTIONS.get(name); + final SchemaFunction function = schemaFunctions.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } @@ -50,7 +89,7 @@ public SchemaFunction schemaFunctionByName(String name) { } public ResultFunction resultFunctionByName(String name) { - final ResultFunction function = RESULT_FUNCTIONS.get(name); + final ResultFunction function = resultFunctions.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java index e417a1da8b5..265fea5b20a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java @@ -11,7 +11,7 @@ public class DataCenterInFunction implements SchemaFunction { - private static final String NAME = "datacenterIn"; + public static final String NAME = "datacenterIn"; private static final String DATACENTERS_FIELD = "datacenters"; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java index cc46b604563..d476c95eba0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java @@ -9,7 +9,7 @@ public class DomainFunction implements SchemaFunction { - private static final String NAME = "domain"; + public static final String NAME = "domain"; @Override public String extract(SchemaFunctionArguments arguments) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java similarity index 95% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java index 4304d6d13aa..e6c8d1b8e05 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidIn.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java @@ -15,7 +15,7 @@ import java.util.Set; import java.util.stream.Collectors; -public class GppSidIn implements SchemaFunction { +public class GppSidInFunction implements SchemaFunction { public static final String NAME = "gppSidIn"; From aa1cc89a945a5c48b8c8e4cff933fcc839ec97c1 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 2 Jun 2025 18:00:02 +0300 Subject: [PATCH 33/94] Small refactorings. --- .../engine/core/config/StageConfigParser.java | 16 +++++++++++++--- .../core/request/RequestSpecification.java | 5 ++--- .../result/functions/ExcludeBiddersFunction.java | 4 ++-- .../engine/core/rules/result/RuleAction.java | 2 ++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index e5f99902177..314e46e2fba 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -139,15 +139,25 @@ private Rule parseDefaultActionRule(ModelGroupConfig conf private List> parseActions(List functionConfigs) { final List> actions = functionConfigs.stream() .map(config -> RuleAction.of( - specification.resultFunctionByName(config.getFunction()), config.getConfig())) + config.getFunction(), + specification.resultFunctionByName(config.getFunction()), + config.getConfig())) .toList(); - actions.forEach(action -> - action.getFunction().validateConfig(action.getConfig())); + actions.forEach(this::validateActionConfig); return actions; } + private void validateActionConfig(RuleAction action) { + try { + action.getFunction().validateConfig(action.getConfig()); + } catch (ConfigurationValidationException exception) { + throw new InvalidMatcherConfiguration( + "Function '%s' configuration is invalid: %s".formatted(action.getName(), exception.getMessage())); + } + } + private Rule combineRules( Rule left, Rule right) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 0bdada2b049..e604d08048c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -30,7 +30,6 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -import org.prebid.server.json.ObjectMapperProvider; import java.util.HashMap; import java.util.Map; @@ -74,8 +73,8 @@ public RequestSpecification(ObjectMapper mapper) { schemaFunctions.put(UserFpdAvailableFunction.NAME, new UserFpdAvailableFunction()); resultFunctions = Map.of( - ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(ObjectMapperProvider.mapper()), - IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(ObjectMapperProvider.mapper())); + ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(mapper), + IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(mapper)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java index fb96bf38c50..42b8551e926 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java @@ -28,11 +28,11 @@ public RuleResult apply(ResultFunctionArguments { + String name; + ResultFunction function; JsonNode config; From 6913355d71c63cdc0535cbae9badf40619a5cca5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 5 Jun 2025 18:08:34 +0300 Subject: [PATCH 34/94] Random weighted rule refinement. --- .../engine/core/config/StageConfigParser.java | 4 +-- .../core/config/model/ModelGroupConfig.java | 2 +- ...ghtedRule.java => RandomWeightedRule.java} | 11 ++----- .../rule/engine/core/util/WeightedEntry.java | 12 ++++---- .../rule/engine/core/util/WeightedList.java | 30 ++++++++++--------- 5 files changed, 27 insertions(+), 32 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{WeightedRule.java => RandomWeightedRule.java} (53%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 314e46e2fba..72a1de19dc1 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -12,10 +12,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; -import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidMatcherConfiguration; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; @@ -64,7 +64,7 @@ private Rule parseModelGroupConfigs(List WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); - return new WeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); + return new RandomWeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } private Rule parseModelGroupConfig(ModelGroupConfig config) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index 7b304385eee..4504ffcb3d5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -8,7 +8,7 @@ @Value(staticConstructor = "of") public class ModelGroupConfig { - double weight; + int weight; @JsonProperty("analyticsKey") String analyticsKey; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java similarity index 53% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java index e2d3c30dc22..66113fd6b59 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java @@ -1,27 +1,22 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; -import org.prebid.server.hooks.modules.rule.engine.core.util.NoMatchingValueException; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import java.util.Objects; import java.util.random.RandomGenerator; -public class WeightedRule implements Rule { +public class RandomWeightedRule implements Rule { private final RandomGenerator random; private final WeightedList> weightedList; - public WeightedRule(RandomGenerator random, WeightedList> weightedList) { + public RandomWeightedRule(RandomGenerator random, WeightedList> weightedList) { this.random = Objects.requireNonNull(random); this.weightedList = Objects.requireNonNull(weightedList); } @Override public RuleResult process(T value, C context) { - try { - return weightedList.getForSeed(random.nextDouble()).process(value, context); - } catch (NoMatchingValueException e) { - return RuleResult.unaltered(value); - } + return weightedList.getForSeed(random.nextInt(weightedList.maxSeed() + 1)).process(value, context); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java index c55833550ed..0edbccce8eb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java @@ -5,22 +5,20 @@ @Getter public class WeightedEntry { - public static final double MAX_WEIGHT = 1.0; - - double weight; + int weight; T value; - private WeightedEntry(double weight, T value) { + private WeightedEntry(int weight, T value) { this.weight = weight; this.value = value; - if (weight < 0 || weight > MAX_WEIGHT) { - throw new IllegalArgumentException("Weight must be between 0 and " + MAX_WEIGHT); + if (weight < 0) { + throw new IllegalArgumentException("Weight must be greater than zero"); } } - public static WeightedEntry of(double weight, T value) { + public static WeightedEntry of(int weight, T value) { return new WeightedEntry<>(weight, value); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java index 239b9b5f274..4aa71a15c2d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java @@ -1,38 +1,36 @@ package org.prebid.server.hooks.modules.rule.engine.core.util; +import org.springframework.util.CollectionUtils; + import java.util.ArrayList; import java.util.List; -import java.util.Objects; public class WeightedList { - private static final double EPSILON = 1e-6; - private final List> entries; + private final int weightSum; public WeightedList(List> entries) { validateEntries(entries); + this.weightSum = entries.stream().mapToInt(WeightedEntry::getWeight).sum(); this.entries = prepareEntries(entries); } private void validateEntries(List> entries) { - Objects.requireNonNull(entries); - - if (entries.isEmpty()) { + if (CollectionUtils.isEmpty(entries)) { throw new IllegalArgumentException("Weighted list cannot be empty"); } - final double sum = entries.stream().mapToDouble(WeightedEntry::getWeight).sum(); - if (sum > WeightedEntry.MAX_WEIGHT + EPSILON) { - throw new IllegalArgumentException( - "Weighted list weights sum must be less than " + WeightedEntry.MAX_WEIGHT); + if (entries.stream().anyMatch(weightedEntry -> weightedEntry.getWeight() <= 0)) { + throw new IllegalArgumentException("Weighted entry should have weight greater than zero"); } } private List> prepareEntries(List> entries) { final List> result = new ArrayList<>(entries.size()); - double cumulativeSum = 0; + + int cumulativeSum = 0; for (WeightedEntry entry : entries) { cumulativeSum += entry.getWeight(); @@ -42,9 +40,9 @@ private List> prepareEntries(List> entries) { return result; } - public T getForSeed(double seed) { - if (seed < 0 || seed > WeightedEntry.MAX_WEIGHT) { - throw new IllegalArgumentException("Seed number must be between 0 and " + WeightedEntry.MAX_WEIGHT); + public T getForSeed(int seed) { + if (seed < 0 || seed > maxSeed()) { + throw new IllegalArgumentException("Seed number must be between 0 and " + weightSum); } for (WeightedEntry entry : entries) { @@ -55,4 +53,8 @@ public T getForSeed(double seed) { throw new NoMatchingValueException("No entry found for seed " + seed); } + + public int maxSeed() { + return weightSum; + } } From 13cfd6a2f50bac98a76010151b76d6179be13361 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 13 Jun 2025 06:42:37 +0300 Subject: [PATCH 35/94] Added ability to check rule matched values, almost finished exclude/includeBidders functions. --- .../config/RuleEngineModuleConfiguration.java | 4 +- .../core/request/RequestMatchingRule.java | 50 ++++----- .../core/request/RequestSpecification.java | 12 +- .../functions/ExcludeBiddersFunction.java | 61 +++++----- .../functions/FilterBiddersFunction.java | 104 ++++++++++++++++++ ....java => FilterBiddersFunctionConfig.java} | 2 +- .../functions/IncludeBiddersFunction.java | 62 +++++------ .../engine/core/rules/DefaultActionRule.java | 9 +- .../rules/result/InfrastructureArguments.java | 7 +- .../engine/core/rules/tree/LookupResult.java | 13 +++ .../rule/engine/core/rules/tree/RuleTree.java | 15 ++- 11 files changed, 229 insertions(+), 110 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/{ExcludeBiddersFunctionConfig.java => FilterBiddersFunctionConfig.java} (88%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 0a5bb2b767f..693230fe86c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -3,6 +3,7 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Vertx; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; @@ -30,12 +31,13 @@ RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { @Bean StageConfigParser processedAuctionRequestStageParser( + BidderCatalog bidderCatalog, @Value("${datacenter-region:#{null}}") String datacenterRegion) { return new StageConfigParser<>( () -> ThreadLocalRandom.current().nextLong(), Stage.processed_auction_request, - new RequestSpecification(ObjectMapperProvider.mapper()), + new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog), (schema, ruleTree, modelVersion, analyticsKey) -> new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index 00ac4aa5e4a..34d7aa72b7d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -9,10 +9,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.LookupResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; import java.util.List; @@ -22,9 +22,9 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -public class RequestMatchingRule implements Rule { +import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; - private static final String NULL_MATCHER = "null"; +public class RequestMatchingRule implements Rule { private final Schema schema; private final Set schemaFunctionNames; @@ -73,40 +73,34 @@ private RuleResult processRule(BidRequest bidRequest, String impId, final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( SchemaFunctionArguments.of(requestContext, holder.getConfig()))) - .map(matcher -> StringUtils.defaultIfEmpty(matcher, NULL_MATCHER)) + .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) .toList(); - final Map schemaFunctionResults = IntStream.range(0, matchers.size()) - .boxed() - .collect(Collectors.toMap( - idx -> schemaFunctions.get(idx).getName(), matchers::get, (left, right) -> left)); + final LookupResult> lookupResult = ruleTree.lookup(matchers); + final RuleConfig ruleConfig = lookupResult.getValue(); - final RuleConfig ruleConfig = ruleTree.getValue(matchers); + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(auctionContext) + .schemaFunctionResults(mergeWithSchema(schema, matchers)) + .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) + .ruleFired(ruleConfig.getCondition()) + .analyticsKey(analyticsKey) + .modelVersion(modelVersion) + .build(); return ruleConfig.getActions().stream().reduce( RuleResult.unaltered(bidRequest), (result, action) -> result.mergeWith( - applyAction( - action, - result.getUpdateResult().getValue(), - auctionContext, - schemaFunctionResults, - ruleConfig.getCondition())), + action.getFunction().apply( + ResultFunctionArguments.of(bidRequest, action.getConfig(), infrastructureArguments))), RuleResult::mergeWith); } - private RuleResult applyAction(RuleAction action, - BidRequest bidRequest, - AuctionContext context, - Map schemaFunctionResults, - String condition) { - - final InfrastructureArguments infrastructureArguments = InfrastructureArguments.of( - context, schemaFunctionResults, analyticsKey, condition, modelVersion); - - final ResultFunctionArguments arguments = ResultFunctionArguments.of( - bidRequest, action.getConfig(), infrastructureArguments); - - return action.getFunction().apply(arguments); + private static Map mergeWithSchema(Schema schema, List values) { + return IntStream.range(0, values.size()) + .boxed() + .collect(Collectors.toMap( + idx -> schema.getFunctions().get(idx).getName(), values::get, (left, right) -> left)); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index e604d08048c..42feef9f3f8 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.iab.openrtb.request.BidRequest; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; @@ -33,7 +34,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Set; public class RequestSpecification implements StageSpecification { @@ -41,13 +41,11 @@ public class RequestSpecification implements StageSpecification PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME, MediaTypeInFunction.NAME); - private final ObjectMapper mapper; - private final Map> schemaFunctions; private final Map> resultFunctions; - public RequestSpecification(ObjectMapper mapper) { - this.mapper = Objects.requireNonNull(mapper); + public RequestSpecification(ObjectMapper mapper, + BidderCatalog bidderCatalog) { schemaFunctions = new HashMap<>(); schemaFunctions.put(AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); @@ -73,8 +71,8 @@ public RequestSpecification(ObjectMapper mapper) { schemaFunctions.put(UserFpdAvailableFunction.NAME, new UserFpdAvailableFunction()); resultFunctions = Map.of( - ExcludeBiddersFunction.NAME, ExcludeBiddersFunction.of(mapper), - IncludeBiddersFunction.NAME, IncludeBiddersFunction.of(mapper)); + ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), + IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java index 42b8551e926..3d8180752f0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java @@ -1,46 +1,41 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.iab.openrtb.request.BidRequest; -import lombok.Value; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; -import org.springframework.util.CollectionUtils; - -@Value(staticConstructor = "of") -public class ExcludeBiddersFunction implements ResultFunction { +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Imp; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.model.UpdateResult; - public static final String NAME = "excludeBidders"; +import java.util.List; - ObjectMapper mapper; +public class ExcludeBiddersFunction extends FilterBiddersFunction { - @Override - public RuleResult apply(ResultFunctionArguments arguments) { - return RuleResult.unaltered(arguments.getOperand()); + public static final String NAME = "excludeBidders"; + + public ExcludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCatalog) { + super(objectMapper, bidderCatalog); } @Override - public void validateConfig(JsonNode config) { - final ExcludeBiddersFunctionConfig parsedConfig = parseConfig(config); - if (parsedConfig == null) { - throw new ConfigurationValidationException("Configuration is required, but not provided"); + protected UpdateResult filterBidders(Imp imp, + List bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie) { + + boolean updated = false; + final ObjectNode updatedExt = imp.getExt().deepCopy(); + for (String bidder : bidders) { + if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { + continue; + } + + updatedExt.remove(bidder); + updated = true; } - if (CollectionUtils.isEmpty(parsedConfig.getBidders())) { - throw new ConfigurationValidationException("'bidders' field is required"); - } - } - - private ExcludeBiddersFunctionConfig parseConfig(JsonNode config) { - try { - return mapper.treeToValue(config, ExcludeBiddersFunctionConfig.class); - } catch (JsonProcessingException e) { - throw new ConfigurationValidationException(e.getMessage()); - } + return updated + ? UpdateResult.updated(imp.toBuilder().ext(updatedExt).build()) + : UpdateResult.unaltered(imp); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java new file mode 100644 index 00000000000..b763775b878 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java @@ -0,0 +1,104 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.model.UpdateResult; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public abstract class FilterBiddersFunction implements ResultFunction { + + private final ObjectMapper mapper; + private final BidderCatalog bidderCatalog; + + public FilterBiddersFunction(ObjectMapper mapper, BidderCatalog bidderCatalog) { + this.mapper = Objects.requireNonNull(mapper); + this.bidderCatalog = Objects.requireNonNull(bidderCatalog); + } + + @Override + public RuleResult apply(ResultFunctionArguments arguments) { + final FilterBiddersFunctionConfig config = parseConfig(arguments.getConfig()); + + final BidRequest bidRequest = arguments.getOperand(); + final InfrastructureArguments infrastructureArguments = arguments.getInfrastructureArguments(); + final UidsCookie uidsCookie = infrastructureArguments.getContext().getUidsCookie(); + final Boolean ifSyncedId = config.getIfSyncedId(); + + boolean updated = false; + final List activities = new ArrayList<>(); + final List updatedImps = new ArrayList<>(); + + for (Imp imp : bidRequest.getImp()) { + if (shouldSkipAdUnit(imp, infrastructureArguments)) { + updatedImps.add(imp); + continue; + } + + final UpdateResult impUpdateResult = filterBidders(imp, config.getBidders(), ifSyncedId, uidsCookie); + updatedImps.add(impUpdateResult.getValue()); + updated = updated || impUpdateResult.isUpdated(); + } + + final UpdateResult updateResult = updated + ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) + : UpdateResult.unaltered(bidRequest); + + return RuleResult.of(updateResult, TagsImpl.of(activities)); + } + + private static boolean shouldSkipAdUnit(Imp imp, InfrastructureArguments arguments) { + return imp.getExt() == null + || (arguments.getSchemaFunctionResults().containsKey(AdUnitCodeFunction.NAME) + && !arguments.getSchemaFunctionMatches().get(AdUnitCodeFunction.NAME).equals("*") + && arguments.getSchemaFunctionMatches().get(AdUnitCodeFunction.NAME).equals(imp.getId())); + } + + protected abstract UpdateResult filterBidders(Imp imp, + List bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie); + + protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { + return bidderCatalog.cookieFamilyName(bidder) + .map(uidsCookie::hasLiveUidFrom) + .orElse(false); + } + + @Override + public void validateConfig(JsonNode config) { + final FilterBiddersFunctionConfig parsedConfig = parseConfig(config); + if (parsedConfig == null) { + throw new ConfigurationValidationException("Configuration is required, but not provided"); + } + + if (CollectionUtils.isEmpty(parsedConfig.getBidders())) { + throw new ConfigurationValidationException("'bidders' field is required"); + } + } + + private FilterBiddersFunctionConfig parseConfig(JsonNode config) { + try { + return mapper.treeToValue(config, FilterBiddersFunctionConfig.class); + } catch (JsonProcessingException e) { + throw new ConfigurationValidationException(e.getMessage()); + } + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java similarity index 88% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java index 8c14a64f92c..0a69650dd10 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java @@ -7,7 +7,7 @@ @Builder @Value(staticConstructor = "of") -public class ExcludeBiddersFunctionConfig { +public class FilterBiddersFunctionConfig { List bidders; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java index 4d5810a817b..8ffb1ec69af 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java @@ -1,47 +1,41 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.iab.openrtb.request.BidRequest; -import lombok.Value; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; -import org.springframework.util.CollectionUtils; - -@Value(staticConstructor = "of") -public class IncludeBiddersFunction implements ResultFunction { +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Imp; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.model.UpdateResult; - public static final String NAME = "includeBidders"; +import java.util.List; - ObjectMapper mapper; +public class IncludeBiddersFunction extends FilterBiddersFunction { - @Override - public RuleResult apply(ResultFunctionArguments arguments) { - return RuleResult.unaltered(arguments.getOperand()); + public static final String NAME = "includeBidders"; + + public IncludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCatalog) { + super(objectMapper, bidderCatalog); } @Override - public void validateConfig(JsonNode config) { - final ExcludeBiddersFunctionConfig parsedConfig = parseConfig(config); - if (parsedConfig == null) { - throw new ConfigurationValidationException("Configuration is required for excludeBidders function"); + protected UpdateResult filterBidders(Imp imp, + List bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie) { + + boolean updated = false; + final ObjectNode updatedExt = imp.getExt().deepCopy(); + for (String bidder : bidders) { + if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { + continue; + } + + updatedExt.remove(bidder); + updated = true; } - if (CollectionUtils.isEmpty(parsedConfig.getBidders())) { - throw new ConfigurationValidationException("bidders field is required for excludeBidders function"); - } - } - - private ExcludeBiddersFunctionConfig parseConfig(JsonNode config) { - try { - return mapper.treeToValue(config, ExcludeBiddersFunctionConfig.class); - } catch (JsonProcessingException e) { - throw new ConfigurationValidationException(e.getMessage()); - } + return updated + ? UpdateResult.updated(imp.toBuilder().ext(updatedExt).build()) + : UpdateResult.unaltered(imp); } } - diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index 4dcadde9622..042d155d24f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -42,6 +42,13 @@ private RuleResult applyAction(RuleAction action, T value, C context) { } private InfrastructureArguments infrastructureArguments(C context) { - return InfrastructureArguments.of(context, Collections.emptyMap(), analyticsKey, RULE_NAME, modelVersion); + return InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired(RULE_NAME) + .analyticsKey(analyticsKey) + .modelVersion(modelVersion) + .build(); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 725465ed456..3a9f2e5ee26 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -1,10 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; +import lombok.Builder; import lombok.Value; -import org.prebid.server.auction.model.AuctionContext; import java.util.Map; +@Builder @Value(staticConstructor = "of") public class InfrastructureArguments { @@ -12,9 +13,11 @@ public class InfrastructureArguments { Map schemaFunctionResults; - String analyticsKey; + Map schemaFunctionMatches; String ruleFired; + String analyticsKey; + String modelVersion; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java new file mode 100644 index 00000000000..283b81a9650 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java @@ -0,0 +1,13 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; + +import lombok.Value; + +import java.util.List; + +@Value(staticConstructor = "of") +public class LookupResult { + + T value; + + List matches; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java index d95982d9e91..a5e54ac5e12 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java @@ -4,11 +4,14 @@ import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; +import java.util.ArrayList; import java.util.List; import java.util.Objects; public class RuleTree { + public static String WILDCARD_MATCHER = "*"; + private final RuleNode root; @Getter @@ -19,19 +22,25 @@ public RuleTree(RuleNode root, int depth) { this.depth = depth; } - public T getValue(List path) { + public LookupResult lookup(List path) { + final List matches = new ArrayList<>(); RuleNode next = root; for (String pathPart : path) { next = switch (next) { + case RuleNode.IntermediateNode node -> { + final RuleNode result = node.next(pathPart); + matches.add(result == null ? WILDCARD_MATCHER : pathPart); + yield ObjectUtils.defaultIfNull(result, node.next(WILDCARD_MATCHER)); + } + case RuleNode.LeafNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); - case RuleNode.IntermediateNode node -> ObjectUtils.firstNonNull(node.next(pathPart), node.next("*")); case null -> throw new NoMatchingRuleException(); }; } return switch (next) { - case RuleNode.LeafNode leaf -> leaf.value(); + case RuleNode.LeafNode leaf -> LookupResult.of(leaf.value(), matches); case RuleNode.IntermediateNode ignored -> throw new IllegalArgumentException("Argument count mismatch"); case null -> throw new NoMatchingRuleException(); }; From 3e4ddbabd4422bd3283757a0e39ff47124e615c8 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 18 Jun 2025 17:26:38 +0300 Subject: [PATCH 36/94] Added seatNonBid, finished include/excludeBidders functions. --- .../core/request/RequestMatchingRule.java | 14 +++++- .../core/request/RequestSpecification.java | 5 +- .../functions/IncludeBiddersFunction.java | 41 ---------------- .../{ => filter}/ExcludeBiddersFunction.java | 23 +++++---- .../{ => filter}/FilterBiddersFunction.java | 48 +++++++++++------- .../FilterBiddersFunctionConfig.java | 9 ++-- .../functions/filter/FilterBiddersResult.java | 14 ++++++ .../filter/IncludeBiddersFunction.java | 41 ++++++++++++++++ .../schema/functions/AdUnitCodeFunction.java | 45 ++--------------- .../functions/AdUnitCodeInFunction.java | 46 +++-------------- .../core/request/util/AdUnitCodeUtils.java | 49 +++++++++++++++++++ .../rule/engine/core/rules/RuleResult.java | 10 +++- .../rules/result/InfrastructureArguments.java | 2 + .../auction/model/BidRejectionReason.java | 12 ++++- 14 files changed, 194 insertions(+), 165 deletions(-) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/{ => filter}/ExcludeBiddersFunction.java (58%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/{ => filter}/FilterBiddersFunction.java (66%) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/{ => filter}/FilterBiddersFunctionConfig.java (61%) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index 34d7aa72b7d..205bb9ac2db 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -7,6 +7,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; @@ -26,6 +27,8 @@ public class RequestMatchingRule implements Rule { + private static final String GLOBAL_IMP_ID = "*"; + private final Schema schema; private final Set schemaFunctionNames; private final RuleTree> ruleTree; @@ -54,7 +57,7 @@ public RequestMatchingRule(Schema schema, @Override public RuleResult process(BidRequest bidRequest, AuctionContext context) { return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() - ? processRule(bidRequest, null, context) + ? processRule(bidRequest, GLOBAL_IMP_ID, context) : processPerImpRule(bidRequest, context); } @@ -76,12 +79,19 @@ private RuleResult processRule(BidRequest bidRequest, String impId, .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) .toList(); - final LookupResult> lookupResult = ruleTree.lookup(matchers); + final LookupResult> lookupResult; + try { + lookupResult = ruleTree.lookup(matchers); + } catch (NoMatchingRuleException e) { + return RuleResult.unaltered(bidRequest); + } + final RuleConfig ruleConfig = lookupResult.getValue(); final InfrastructureArguments infrastructureArguments = InfrastructureArguments.builder() .context(auctionContext) + .impId(impId) .schemaFunctionResults(mergeWithSchema(schema, matchers)) .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) .ruleFired(ruleConfig.getCondition()) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 42feef9f3f8..f8d3a23e466 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -4,8 +4,8 @@ import com.iab.openrtb.request.BidRequest; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.bidder.BidderCatalog; -import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.ExcludeBiddersFunction; -import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.IncludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.BundleFunction; @@ -75,7 +75,6 @@ ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); } - public SchemaFunction schemaFunctionByName(String name) { final SchemaFunction function = schemaFunctions.get(name); if (function == null) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java deleted file mode 100644 index 8ffb1ec69af..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/IncludeBiddersFunction.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.iab.openrtb.request.Imp; -import org.prebid.server.bidder.BidderCatalog; -import org.prebid.server.cookie.UidsCookie; -import org.prebid.server.model.UpdateResult; - -import java.util.List; - -public class IncludeBiddersFunction extends FilterBiddersFunction { - - public static final String NAME = "includeBidders"; - - public IncludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCatalog) { - super(objectMapper, bidderCatalog); - } - - @Override - protected UpdateResult filterBidders(Imp imp, - List bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie) { - - boolean updated = false; - final ObjectNode updatedExt = imp.getExt().deepCopy(); - for (String bidder : bidders) { - if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { - continue; - } - - updatedExt.remove(bidder); - updated = true; - } - - return updated - ? UpdateResult.updated(imp.toBuilder().ext(updatedExt).build()) - : UpdateResult.unaltered(imp); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java similarity index 58% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 3d8180752f0..02637aa8e36 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -1,13 +1,13 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Imp; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; -import org.prebid.server.model.UpdateResult; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class ExcludeBiddersFunction extends FilterBiddersFunction { @@ -18,12 +18,12 @@ public ExcludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCat } @Override - protected UpdateResult filterBidders(Imp imp, - List bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie) { + protected FilterBiddersResult filterBidders(Imp imp, + Set bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie) { - boolean updated = false; + final Set removedBidders = new HashSet<>(); final ObjectNode updatedExt = imp.getExt().deepCopy(); for (String bidder : bidders) { if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { @@ -31,11 +31,10 @@ protected UpdateResult filterBidders(Imp imp, } updatedExt.remove(bidder); - updated = true; + removedBidders.add(bidder); } - return updated - ? UpdateResult.updated(imp.toBuilder().ext(updatedExt).build()) - : UpdateResult.unaltered(imp); + final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); + return FilterBiddersResult.of(updatedImp, removedBidders); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java similarity index 66% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index b763775b878..67c81e3bb39 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -1,15 +1,16 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; @@ -17,16 +18,21 @@ import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; import org.prebid.server.hooks.v1.analytics.Activity; import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.springframework.util.CollectionUtils; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; public abstract class FilterBiddersFunction implements ResultFunction { private final ObjectMapper mapper; - private final BidderCatalog bidderCatalog; + protected final BidderCatalog bidderCatalog; public FilterBiddersFunction(ObjectMapper mapper, BidderCatalog bidderCatalog) { this.mapper = Objects.requireNonNull(mapper); @@ -41,40 +47,44 @@ public RuleResult apply(ResultFunctionArguments infrastructureArguments = arguments.getInfrastructureArguments(); final UidsCookie uidsCookie = infrastructureArguments.getContext().getUidsCookie(); final Boolean ifSyncedId = config.getIfSyncedId(); + final BidRejectionReason rejectionReason = config.getSeatNonBid(); + final String impId = infrastructureArguments.getImpId(); - boolean updated = false; final List activities = new ArrayList<>(); final List updatedImps = new ArrayList<>(); + final Set removedBidders = new HashSet<>(); + final List seatNonBid = new ArrayList<>(); for (Imp imp : bidRequest.getImp()) { - if (shouldSkipAdUnit(imp, infrastructureArguments)) { + if (!impId.equals("*") && !StringUtils.equals(impId, imp.getId())) { updatedImps.add(imp); continue; } - final UpdateResult impUpdateResult = filterBidders(imp, config.getBidders(), ifSyncedId, uidsCookie); - updatedImps.add(impUpdateResult.getValue()); - updated = updated || impUpdateResult.isUpdated(); + final FilterBiddersResult result = filterBidders(imp, config.getBidders(), ifSyncedId, uidsCookie); + updatedImps.add(result.getImp()); + removedBidders.addAll(result.getBidders()); + seatNonBid.addAll(toSeatNonBid(result, rejectionReason)); } - final UpdateResult updateResult = updated + final UpdateResult updateResult = removedBidders.isEmpty() ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) : UpdateResult.unaltered(bidRequest); - return RuleResult.of(updateResult, TagsImpl.of(activities)); + return RuleResult.of(updateResult, TagsImpl.of(activities), seatNonBid); } - private static boolean shouldSkipAdUnit(Imp imp, InfrastructureArguments arguments) { - return imp.getExt() == null - || (arguments.getSchemaFunctionResults().containsKey(AdUnitCodeFunction.NAME) - && !arguments.getSchemaFunctionMatches().get(AdUnitCodeFunction.NAME).equals("*") - && arguments.getSchemaFunctionMatches().get(AdUnitCodeFunction.NAME).equals(imp.getId())); + private static List toSeatNonBid(FilterBiddersResult filterBiddersResult, BidRejectionReason reason) { + final String impId = filterBiddersResult.getImp().getId(); + return filterBiddersResult.getBidders().stream() + .map(bidder -> SeatNonBid.of(bidder, Collections.singletonList(NonBid.of(impId, reason)))) + .toList(); } - protected abstract UpdateResult filterBidders(Imp imp, - List bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie); + protected abstract FilterBiddersResult filterBidders(Imp imp, + Set bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie); protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { return bidderCatalog.cookieFamilyName(bidder) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java similarity index 61% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index 0a69650dd10..2c954ccfce7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/FilterBiddersFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -1,18 +1,19 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions; +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import lombok.Builder; import lombok.Value; +import org.prebid.server.auction.model.BidRejectionReason; -import java.util.List; +import java.util.Set; @Builder @Value(staticConstructor = "of") public class FilterBiddersFunctionConfig { - List bidders; + Set bidders; @Builder.Default - Integer seatNonBid = 203; + BidRejectionReason seatNonBid = 203; Boolean ifSyncedId; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java new file mode 100644 index 00000000000..47e946213f9 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.iab.openrtb.request.Imp; +import lombok.Value; + +import java.util.Set; + +@Value(staticConstructor = "of") +public class FilterBiddersResult { + + Imp imp; + + Set bidders; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java new file mode 100644 index 00000000000..86807a18754 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -0,0 +1,41 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Imp; +import org.apache.commons.collections4.IteratorUtils; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; + +import java.util.HashSet; +import java.util.Set; + +public class IncludeBiddersFunction extends FilterBiddersFunction { + + public static final String NAME = "includeBidders"; + + public IncludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCatalog) { + super(objectMapper, bidderCatalog); + } + + @Override + protected FilterBiddersResult filterBidders(Imp imp, + Set bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie) { + + final Set removedBidders = new HashSet<>(); + final ObjectNode ext = imp.getExt(); + final ObjectNode updatedExt = ext.deepCopy(); + + for (String bidder : IteratorUtils.asIterable(ext.fieldNames())) { + if (!bidders.contains(bidder)) { + updatedExt.remove(bidder); + removedBidders.add(bidder); + } + } + + final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); + return FilterBiddersResult.of(updatedImp, removedBidders); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index d9311cade9e..e6e66b97add 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -1,18 +1,15 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -import java.util.Optional; - public class AdUnitCodeFunction implements SchemaFunction { public static final String NAME = "adUnitCode"; @@ -23,49 +20,13 @@ public String extract(SchemaFunctionArguments arguments) { final String impId = context.getImpId(); final BidRequest bidRequest = context.getBidRequest(); - final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() + return ListUtils.emptyIfNull(bidRequest.getImp()).stream() .filter(imp -> StringUtils.equals(imp.getId(), impId)) .findFirst() - .orElseThrow(() -> new IllegalStateException( - "Critical error in rules engine. Imp id of absent imp supplied")); - - - return extractGpid(adUnit) - .or(() -> extractTagId(adUnit)) - .or(() -> extractPbAdSlot(adUnit)) - .or(() -> extractStoredRequestId(adUnit)) + .flatMap(AdUnitCodeUtils::extractAdUnitCode) .orElse(UNDEFINED_RESULT); } - private static Optional extractGpid(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("gpid")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText); - } - - private static Optional extractTagId(Imp imp) { - return Optional.ofNullable(imp.getTagid()) - .filter(StringUtils::isNotBlank); - } - - private static Optional extractPbAdSlot(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("data")) - .map(data -> data.get("pbadslot")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText); - } - - private static Optional extractStoredRequestId(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("prebid")) - .map(prebid -> prebid.get("storedrequest")) - .map(storedRequest -> storedRequest.get("id")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText); - } - @Override public void validateConfig(ObjectNode config) { ValidationUtils.assertNoArgs(config); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 2957770077d..6bb20d96453 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -6,13 +6,13 @@ import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.util.StreamUtil; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -37,11 +37,12 @@ public String extract(SchemaFunctionArguments arguments) { "Critical error in rules engine. Imp id of absent imp supplied")); final Set adUnitPotentialCodes = Stream.of( - extractGpid(adUnit), - extractTagId(adUnit), - extractPbAdSlot(adUnit), - extractStoredRequestId(adUnit)) - .filter(Objects::nonNull) + AdUnitCodeUtils.extractGpid(adUnit), + AdUnitCodeUtils.extractTagId(adUnit), + AdUnitCodeUtils.extractPbAdSlot(adUnit), + AdUnitCodeUtils.extractStoredRequestId(adUnit)) + .filter(Optional::isPresent) + .map(Optional::get) .collect(Collectors.toSet()); final boolean matches = StreamUtil.asStream(arguments.getConfig().get(CODES_FIELD).elements()) @@ -51,39 +52,6 @@ public String extract(SchemaFunctionArguments arguments) { return Boolean.toString(matches); } - private static String extractGpid(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("gpid")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText) - .orElse(null); - } - - private static String extractTagId(Imp imp) { - return Optional.ofNullable(imp.getTagid()) - .filter(StringUtils::isNotBlank) - .orElse(null); - } - - private static String extractPbAdSlot(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("data")) - .map(data -> data.get("pbadslot")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText) - .orElse(null); - } - - private static String extractStoredRequestId(Imp imp) { - return Optional.ofNullable(imp.getExt()) - .map(ext -> ext.get("prebid")) - .map(prebid -> prebid.get("storedrequest")) - .map(storedRequest -> storedRequest.get("id")) - .filter(JsonNode::isTextual) - .map(JsonNode::asText) - .orElse(null); - } - @Override public void validateConfig(ObjectNode config) { ValidationUtils.assertArrayOfStrings(config, CODES_FIELD); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java new file mode 100644 index 00000000000..11c3566c1e2 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java @@ -0,0 +1,49 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.iab.openrtb.request.Imp; +import org.apache.commons.lang3.StringUtils; + +import java.util.Optional; + +public class AdUnitCodeUtils { + + private AdUnitCodeUtils() { + } + + public static Optional extractAdUnitCode(Imp imp) { + return extractGpid(imp) + .or(() -> extractTagId(imp)) + .or(() -> extractPbAdSlot(imp)) + .or(() -> extractStoredRequestId(imp)); + } + + public static Optional extractGpid(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("gpid")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } + + public static Optional extractTagId(Imp imp) { + return Optional.ofNullable(imp.getTagid()) + .filter(StringUtils::isNotBlank); + } + + public static Optional extractPbAdSlot(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("data")) + .map(data -> data.get("pbadslot")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } + + public static Optional extractStoredRequestId(Imp imp) { + return Optional.ofNullable(imp.getExt()) + .map(ext -> ext.get("prebid")) + .map(prebid -> prebid.get("storedrequest")) + .map(storedRequest -> storedRequest.get("id")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index 789c97b92f4..6138703caae 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -4,9 +4,11 @@ import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.v1.analytics.Tags; import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.prebid.server.util.ListUtil; import java.util.Collections; +import java.util.List; @Value(staticConstructor = "of") public class RuleResult { @@ -15,15 +17,19 @@ public class RuleResult { Tags analyticsTags; + List seatNonBid; + public RuleResult mergeWith(RuleResult other) { final boolean updated = other.updateResult.isUpdated() || updateResult.isUpdated(); final T value = other.updateResult.getValue(); final Tags tags = TagsImpl.of(ListUtil.union(analyticsTags.activities(), other.analyticsTags.activities())); + final List seatNonBids = ListUtil.union(seatNonBid, other.seatNonBid); - return RuleResult.of(UpdateResult.of(updated, value), tags); + return RuleResult.of(UpdateResult.of(updated, value), tags, seatNonBids); } public static RuleResult unaltered(T value) { - return RuleResult.of(UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList())); + return RuleResult.of( + UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList()), Collections.emptyList()); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 3a9f2e5ee26..155cef12a79 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -17,6 +17,8 @@ public class InfrastructureArguments { String ruleFired; + String impId; + String analyticsKey; String modelVersion; diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java index fc3ee36bd2a..c6e399e65af 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java @@ -1,7 +1,10 @@ package org.prebid.server.auction.model; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Arrays; + /** * The list of the Seat Non Bid codes: * 0 - the bidder is called but declines to bid and doesn't provide a code (for the impression) @@ -112,9 +115,16 @@ public enum BidRejectionReason { this.code = code; } + @JsonCreator + public static BidRejectionReason fromStatusCode(int code) { + return Arrays.stream(values()) + .filter(e -> e.code == code) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Invalid bid rejection reason: " + code)); + } + @JsonValue public int getValue() { return code; } - } From fa29aedc8fce308b14143f27d5f5d7e739809440 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 18 Jun 2025 18:15:05 +0300 Subject: [PATCH 37/94] Moved impId to result function context to generify result function arguments. --- .../config/RuleEngineModuleConfiguration.java | 9 ++-- .../core/config/AccountConfigParser.java | 10 +++-- .../config/model/ResultFunctionConfig.java | 4 +- .../core/request/RequestMatchingRule.java | 44 ++++++++++--------- .../core/request/RequestSpecification.java | 22 ++++++---- .../request/context/RequestResultContext.java | 12 +++++ .../RequestSchemaContext.java} | 4 +- .../filter/FilterBiddersFunction.java | 17 ++++--- .../filter/FilterBiddersFunctionConfig.java | 2 +- .../schema/functions/AdUnitCodeFunction.java | 8 ++-- .../functions/AdUnitCodeInFunction.java | 8 ++-- .../schema/functions/BundleFunction.java | 6 +-- .../schema/functions/BundleInFunction.java | 6 +-- .../schema/functions/ChannelFunction.java | 6 +-- .../schema/functions/DataCenterFunction.java | 6 +-- .../functions/DataCenterInFunction.java | 6 +-- .../functions/DeviceCountryFunction.java | 6 +-- .../functions/DeviceCountryInFunction.java | 6 +-- .../schema/functions/DeviceTypeFunction.java | 6 +-- .../functions/DeviceTypeInFunction.java | 6 +-- .../schema/functions/DomainFunction.java | 6 +-- .../functions/EidAvailableFunction.java | 6 +-- .../schema/functions/EidInFunction.java | 6 +-- .../functions/FpdAvailableFunction.java | 6 +-- .../functions/GppSidAvailableFunction.java | 6 +-- .../schema/functions/GppSidInFunction.java | 6 +-- .../schema/functions/MediaTypeInFunction.java | 8 ++-- .../schema/functions/PrebidKeyFunction.java | 6 +-- .../schema/functions/TcfInScopeFunction.java | 6 +-- .../functions/UserFpdAvailableFunction.java | 6 +-- .../rule/engine/core/rules/NoOpRule.java | 4 +- .../rule/engine/core/rules/PerStageRule.java | 4 +- .../rules/result/InfrastructureArguments.java | 2 - .../core/rules/result/ResultFunction.java | 4 +- .../engine/core/rules/result/RuleAction.java | 4 +- ...RuleEngineProcessedAuctionRequestHook.java | 9 ++-- 36 files changed, 153 insertions(+), 130 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{RequestContext.java => context/RequestSchemaContext.java} (83%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index 693230fe86c..cb73a75bcba 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -2,14 +2,14 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Vertx; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; @@ -30,7 +30,7 @@ RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { } @Bean - StageConfigParser processedAuctionRequestStageParser( + StageConfigParser processedAuctionRequestStageParser( BidderCatalog bidderCatalog, @Value("${datacenter-region:#{null}}") String datacenterRegion) { @@ -44,7 +44,8 @@ StageConfigParser processedAuctionRe @Bean AccountConfigParser accountConfigParser( - StageConfigParser processedAuctionRequestStageParser) { + StageConfigParser< + RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser) { return new AccountConfigParser(ObjectMapperProvider.mapper(), processedAuctionRequestStageParser); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index cb1ecbd117d..59b7a06ece9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -4,22 +4,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; import java.util.Objects; public class AccountConfigParser { private final ObjectMapper mapper; - private final StageConfigParser processedAuctionRequestStageParser; + private final StageConfigParser< + RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser; public AccountConfigParser( ObjectMapper mapper, - StageConfigParser processedAuctionRequestStageParser) { + StageConfigParser< + RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser) { this.mapper = Objects.requireNonNull(mapper); this.processedAuctionRequestStageParser = Objects.requireNonNull(processedAuctionRequestStageParser); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java index acd9fa6d99b..9daf2811a76 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.config.model; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; @Value(staticConstructor = "of") @@ -8,5 +8,5 @@ public class ResultFunctionConfig { String function; - JsonNode config; + ObjectNode config; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index 205bb9ac2db..0547bbde474 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -4,6 +4,8 @@ import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -25,20 +27,20 @@ import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; -public class RequestMatchingRule implements Rule { +public class RequestMatchingRule implements Rule { private static final String GLOBAL_IMP_ID = "*"; - private final Schema schema; + private final Schema schema; private final Set schemaFunctionNames; - private final RuleTree> ruleTree; + private final RuleTree> ruleTree; private final String modelVersion; private final String analyticsKey; private final String datacenter; - public RequestMatchingRule(Schema schema, - RuleTree> ruleTree, + public RequestMatchingRule(Schema schema, + RuleTree> ruleTree, String modelVersion, String analyticsKey, String datacenter) { @@ -55,43 +57,45 @@ public RequestMatchingRule(Schema schema, } @Override - public RuleResult process(BidRequest bidRequest, AuctionContext context) { + public RuleResult process(BidRequest bidRequest, RequestResultContext context) { return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() - ? processRule(bidRequest, GLOBAL_IMP_ID, context) + ? processRule(bidRequest, GLOBAL_IMP_ID, context.getAuctionContext()) : processPerImpRule(bidRequest, context); } - private RuleResult processPerImpRule(BidRequest bidRequest, AuctionContext context) { + private RuleResult processPerImpRule(BidRequest bidRequest, RequestResultContext context) { return bidRequest.getImp().stream().reduce( RuleResult.unaltered(bidRequest), - (result, imp) -> - result.mergeWith(processRule(result.getUpdateResult().getValue(), imp.getId(), context)), + (result, imp) -> result.mergeWith( + processRule( + result.getUpdateResult().getValue(), + imp.getId(), + context.getAuctionContext())), RuleResult::mergeWith); } private RuleResult processRule(BidRequest bidRequest, String impId, AuctionContext auctionContext) { - final RequestContext requestContext = RequestContext.of(bidRequest, impId, datacenter); + final RequestSchemaContext schemaFunctionContext = RequestSchemaContext.of(bidRequest, impId, datacenter); - final List> schemaFunctions = schema.getFunctions(); + final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(requestContext, holder.getConfig()))) + SchemaFunctionArguments.of(schemaFunctionContext, holder.getConfig()))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) .toList(); - final LookupResult> lookupResult; + final LookupResult> lookupResult; try { lookupResult = ruleTree.lookup(matchers); } catch (NoMatchingRuleException e) { return RuleResult.unaltered(bidRequest); } - final RuleConfig ruleConfig = lookupResult.getValue(); + final RuleConfig ruleConfig = lookupResult.getValue(); - final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() - .context(auctionContext) - .impId(impId) + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(RequestResultContext.of(auctionContext, impId)) .schemaFunctionResults(mergeWithSchema(schema, matchers)) .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) .ruleFired(ruleConfig.getCondition()) @@ -107,7 +111,7 @@ private RuleResult processRule(BidRequest bidRequest, String impId, RuleResult::mergeWith); } - private static Map mergeWithSchema(Schema schema, List values) { + private static Map mergeWithSchema(Schema schema, List values) { return IntStream.range(0, values.size()) .boxed() .collect(Collectors.toMap( diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index f8d3a23e466..a5a7d724957 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -2,8 +2,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; @@ -36,13 +38,14 @@ import java.util.Map; import java.util.Set; -public class RequestSpecification implements StageSpecification { +public class RequestSpecification implements + StageSpecification { public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME, MediaTypeInFunction.NAME); - private final Map> schemaFunctions; - private final Map> resultFunctions; + private final Map> schemaFunctions; + private final Map> resultFunctions; public RequestSpecification(ObjectMapper mapper, BidderCatalog bidderCatalog) { @@ -72,11 +75,12 @@ public RequestSpecification(ObjectMapper mapper, resultFunctions = Map.of( ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), - IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); + IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog), + LogATagFunction.NAME, new LogATagFunction(mapper)); } - public SchemaFunction schemaFunctionByName(String name) { - final SchemaFunction function = schemaFunctions.get(name); + public SchemaFunction schemaFunctionByName(String name) { + final SchemaFunction function = schemaFunctions.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } @@ -84,8 +88,8 @@ public SchemaFunction schemaFunctionByName(String name) { return function; } - public ResultFunction resultFunctionByName(String name) { - final ResultFunction function = resultFunctions.get(name); + public ResultFunction resultFunctionByName(String name) { + final ResultFunction function = resultFunctions.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java new file mode 100644 index 00000000000..db4cc153063 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java @@ -0,0 +1,12 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.context; + +import lombok.Value; +import org.prebid.server.auction.model.AuctionContext; + +@Value(staticConstructor = "of") +public class RequestResultContext { + + AuctionContext auctionContext; + + String impId; +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java similarity index 83% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java index f2d343523e8..bbbe8aaf1bb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestContext.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java @@ -1,10 +1,10 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; +package org.prebid.server.hooks.modules.rule.engine.core.request.context; import com.iab.openrtb.request.BidRequest; import lombok.Value; @Value(staticConstructor = "of") -public class RequestContext { +public class RequestSchemaContext { BidRequest bidRequest; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 67c81e3bb39..ff1d7e4e49f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -3,14 +3,15 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; @@ -29,7 +30,7 @@ import java.util.Objects; import java.util.Set; -public abstract class FilterBiddersFunction implements ResultFunction { +public abstract class FilterBiddersFunction implements ResultFunction { private final ObjectMapper mapper; protected final BidderCatalog bidderCatalog; @@ -40,15 +41,17 @@ public FilterBiddersFunction(ObjectMapper mapper, BidderCatalog bidderCatalog) { } @Override - public RuleResult apply(ResultFunctionArguments arguments) { + public RuleResult apply(ResultFunctionArguments arguments) { final FilterBiddersFunctionConfig config = parseConfig(arguments.getConfig()); final BidRequest bidRequest = arguments.getOperand(); - final InfrastructureArguments infrastructureArguments = arguments.getInfrastructureArguments(); - final UidsCookie uidsCookie = infrastructureArguments.getContext().getUidsCookie(); + final InfrastructureArguments infrastructureArguments = + arguments.getInfrastructureArguments(); + + final UidsCookie uidsCookie = infrastructureArguments.getContext().getAuctionContext().getUidsCookie(); final Boolean ifSyncedId = config.getIfSyncedId(); final BidRejectionReason rejectionReason = config.getSeatNonBid(); - final String impId = infrastructureArguments.getImpId(); + final String impId = infrastructureArguments.getContext().getImpId(); final List activities = new ArrayList<>(); final List updatedImps = new ArrayList<>(); @@ -93,7 +96,7 @@ protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { } @Override - public void validateConfig(JsonNode config) { + public void validateConfig(ObjectNode config) { final FilterBiddersFunctionConfig parsedConfig = parseConfig(config); if (parsedConfig == null) { throw new ConfigurationValidationException("Configuration is required, but not provided"); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index 2c954ccfce7..c489b86dcba 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -13,7 +13,7 @@ public class FilterBiddersFunctionConfig { Set bidders; @Builder.Default - BidRejectionReason seatNonBid = 203; + BidRejectionReason seatNonBid; Boolean ifSyncedId; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index e6e66b97add..624c7eddb9d 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -5,18 +5,18 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class AdUnitCodeFunction implements SchemaFunction { +public class AdUnitCodeFunction implements SchemaFunction { public static final String NAME = "adUnitCode"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestContext context = arguments.getOperand(); + public String extract(SchemaFunctionArguments arguments) { + final RequestSchemaContext context = arguments.getOperand(); final String impId = context.getImpId(); final BidRequest bidRequest = context.getBidRequest(); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 6bb20d96453..658bcbf5626 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -7,7 +7,7 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -18,15 +18,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class AdUnitCodeInFunction implements SchemaFunction { +public class AdUnitCodeInFunction implements SchemaFunction { public static final String NAME = "adUnitCodeIn"; private static final String CODES_FIELD = "codes"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestContext context = arguments.getOperand(); + public String extract(SchemaFunctionArguments arguments) { + final RequestSchemaContext context = arguments.getOperand(); final String impId = context.getImpId(); final BidRequest bidRequest = context.getBidRequest(); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java index b5407c1bdae..b98220f57ec 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java @@ -2,19 +2,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class BundleFunction implements SchemaFunction { +public class BundleFunction implements SchemaFunction { public static final String NAME = "bundle"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { return Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) .map(App::getBundle) .orElse(UNDEFINED_RESULT); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java index fd35c184e49..ac907c7e745 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -11,14 +11,14 @@ import java.util.Optional; -public class BundleInFunction implements SchemaFunction { +public class BundleInFunction implements SchemaFunction { public static final String NAME = "bundleIn"; private static final String BUNDLES_FIELD = "bundles"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final String bundle = Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) .map(App::getBundle) .orElse(UNDEFINED_RESULT); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java index 3f02a589f17..098c2125bd6 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,12 +12,12 @@ import java.util.Optional; -public class ChannelFunction implements SchemaFunction { +public class ChannelFunction implements SchemaFunction { public static final String NAME = "channel"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { return Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getExt) .map(ExtRequest::getPrebid) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java index d9c69ebacf5..1c815844326 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java @@ -2,17 +2,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class DataCenterFunction implements SchemaFunction { +public class DataCenterFunction implements SchemaFunction { public static final String NAME = "datacenter"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { return StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java index 265fea5b20a..39edb5bce99 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java @@ -3,20 +3,20 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.util.StreamUtil; -public class DataCenterInFunction implements SchemaFunction { +public class DataCenterInFunction implements SchemaFunction { public static final String NAME = "datacenterIn"; private static final String DATACENTERS_FIELD = "datacenters"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final String datacenter = StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); final boolean matches = StreamUtil.asStream(arguments.getConfig().get(DATACENTERS_FIELD).elements()) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java index c0d35e70581..8c0215cb4a7 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java @@ -4,19 +4,19 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class DeviceCountryFunction implements SchemaFunction { +public class DeviceCountryFunction implements SchemaFunction { public static final String NAME = "deviceCountry"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { return Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getDevice) .map(Device::getGeo) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java index f4f6064327f..42826146eae 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java @@ -5,7 +5,7 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -13,13 +13,13 @@ import java.util.Optional; -public class DeviceCountryInFunction implements SchemaFunction { +public class DeviceCountryInFunction implements SchemaFunction { public static final String NAME = "deviceCountryIn"; private static final String COUNTRIES_FIELD = "countries"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final String deviceCountry = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getDevice) .map(Device::getGeo) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java index 973b33484ed..8772d83fcd9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java @@ -2,19 +2,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Device; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class DeviceTypeFunction implements SchemaFunction { +public class DeviceTypeFunction implements SchemaFunction { public static final String NAME = "deviceType"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { return Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) .map(Device::getDevicetype) .map(String::valueOf) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java index e7a3e20bd89..8c9d7172322 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Device; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -11,14 +11,14 @@ import java.util.Optional; -public class DeviceTypeInFunction implements SchemaFunction { +public class DeviceTypeInFunction implements SchemaFunction { public static final String NAME = "deviceTypeIn"; public static final String TYPES_FIELD = "types"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final Integer deviceType = Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) .map(Device::getDevicetype) .orElse(null); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java index d476c95eba0..5672aad51fa 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java @@ -2,17 +2,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class DomainFunction implements SchemaFunction { +public class DomainFunction implements SchemaFunction { public static final String NAME = "domain"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final BidRequest bidRequest = arguments.getOperand().getBidRequest(); return UNDEFINED_RESULT; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java index e60f456b2d2..b694f8f7f25 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,12 +12,12 @@ import java.util.Optional; import java.util.function.Predicate; -public class EidAvailableFunction implements SchemaFunction { +public class EidAvailableFunction implements SchemaFunction { public static final String NAME = "eidAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final boolean available = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getUser) .map(User::getEids) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java index 2783724df3c..8d11f8f2691 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java @@ -5,7 +5,7 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Eid; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -16,14 +16,14 @@ import java.util.Set; import java.util.stream.Collectors; -public class EidInFunction implements SchemaFunction { +public class EidInFunction implements SchemaFunction { public static final String NAME = "eidIn"; private static final String SOURCES_FIELD = "sources"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final Set sources = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getUser) .map(User::getEids) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java index a92524d80ac..55c3370ed96 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java @@ -6,7 +6,7 @@ import com.iab.openrtb.request.Content; import com.iab.openrtb.request.Site; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -18,12 +18,12 @@ import java.util.Optional; import java.util.function.Predicate; -public class FpdAvailableFunction implements SchemaFunction { +public class FpdAvailableFunction implements SchemaFunction { public static final String NAME = "fpdAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final BidRequest bidRequest = arguments.getOperand().getBidRequest(); final boolean available = isUserDataAvailable(bidRequest) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java index a183103f4d2..10b04c107f9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,12 +12,12 @@ import java.util.Objects; import java.util.Optional; -public class GppSidAvailableFunction implements SchemaFunction { +public class GppSidAvailableFunction implements SchemaFunction { public static final String NAME = "gppSidAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final boolean available = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getRegs) .map(Regs::getGppSid) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java index e6c8d1b8e05..992fde8bfb0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -15,14 +15,14 @@ import java.util.Set; import java.util.stream.Collectors; -public class GppSidInFunction implements SchemaFunction { +public class GppSidInFunction implements SchemaFunction { public static final String NAME = "gppSidIn"; private static final String SIDS_FIELD = "sids"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final Set sids = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getRegs) .map(Regs::getGppSid) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index f31baa3d9ec..df0b29d1bd4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -6,7 +6,7 @@ import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -16,15 +16,15 @@ import java.util.HashSet; import java.util.Set; -public class MediaTypeInFunction implements SchemaFunction { +public class MediaTypeInFunction implements SchemaFunction { public static final String NAME = "mediaTypeIn"; private static final String TYPES_FIELD = "types"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestContext context = arguments.getOperand(); + public String extract(SchemaFunctionArguments arguments) { + final RequestSchemaContext context = arguments.getOperand(); final String impId = context.getImpId(); final BidRequest bidRequest = context.getBidRequest(); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java index 84e8ebdb453..9e71e30eb43 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java @@ -1,19 +1,19 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class PrebidKeyFunction implements SchemaFunction { +public class PrebidKeyFunction implements SchemaFunction { public static final String NAME = "prebidKey"; private static final String KEY_FIELD = "key"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { // final String key = arguments.getConfig().get(KEY_FIELD).asText(); // return Optional.ofNullable(arguments.getOperand().getBidRequest().getExt()) // .map(ext -> ext.getPrebid()) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java index 3f82e221d36..a9b513026ee 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java @@ -3,19 +3,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class TcfInScopeFunction implements SchemaFunction { +public class TcfInScopeFunction implements SchemaFunction { public static final String NAME = "tcfInScope"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final boolean inScope = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getRegs) .map(Regs::getGdpr) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java index 3960388939f..eb438a766e5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -13,12 +13,12 @@ import java.util.Optional; import java.util.function.Predicate; -public class UserFpdAvailableFunction implements SchemaFunction { +public class UserFpdAvailableFunction implements SchemaFunction { public static final String NAME = "userFpdAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final Optional user = Optional.of(arguments.getOperand().getBidRequest()) .map(BidRequest::getUser); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java index fa5cca636d5..6be471ddfcd 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -1,7 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.NoArgsConstructor; -import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.model.UpdateResult; @@ -12,6 +11,7 @@ public class NoOpRule implements Rule { @Override public RuleResult process(T value, C context) { - return RuleResult.of(UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList())); + return RuleResult.of( + UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList()), Collections.emptyList()); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index d15b90bce0e..ca94640135b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -4,13 +4,13 @@ import lombok.Builder; import lombok.Value; import lombok.experimental.Accessors; -import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; @Builder @Accessors(fluent = true) @Value(staticConstructor = "of") public class PerStageRule { - Rule processedAuctionRequestRule; + Rule processedAuctionRequestRule; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 155cef12a79..3a9f2e5ee26 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -17,8 +17,6 @@ public class InfrastructureArguments { String ruleFired; - String impId; - String analyticsKey; String modelVersion; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java index 092eb182128..831a85c6af9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java @@ -1,11 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; public interface ResultFunction { RuleResult apply(ResultFunctionArguments arguments); - void validateConfig(JsonNode config); + void validateConfig(ObjectNode config); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java index c8b0bc4c001..29516b291ed 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; @Value(staticConstructor = "of") @@ -10,5 +10,5 @@ public class RuleAction { ResultFunction function; - JsonNode config; + ObjectNode config; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 2c83dde6cf3..8f577ef81cb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -4,9 +4,9 @@ import io.vertx.core.Future; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -18,7 +18,6 @@ import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; import org.prebid.server.model.UpdateResult; -import java.util.Collections; import java.util.Objects; public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -44,13 +43,13 @@ public Future> call(AuctionRequestPayloa .recover(RuleEngineProcessedAuctionRequestHook::failure); } - private static RuleResult executeSafely(Rule rule, + private static RuleResult executeSafely(Rule rule, BidRequest bidRequest, AuctionContext context) { return rule != null - ? rule.process(bidRequest, context) - : RuleResult.of(UpdateResult.unaltered(bidRequest), TagsImpl.of(Collections.emptyList())); + ? rule.process(bidRequest, RequestResultContext.of(context, "*")) + : RuleResult.unaltered(bidRequest); } private static InvocationResult succeeded(RuleResult result) { From d0bdc2a6cc2de43b85f4a7b55356f1121d9938bd Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 11 Jul 2025 13:21:46 +0300 Subject: [PATCH 38/94] Changed result function config field from config to args, fixed filtering result function. --- .../rule/engine/core/config/StageConfigParser.java | 2 +- .../core/config/model/ResultFunctionConfig.java | 2 +- .../engine/core/request/RequestSpecification.java | 4 +--- .../functions/filter/ExcludeBiddersFunction.java | 3 ++- .../functions/filter/FilterBiddersFunction.java | 11 ++++++++++- .../functions/filter/IncludeBiddersFunction.java | 2 +- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 72a1de19dc1..83380947b84 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -141,7 +141,7 @@ private List> parseActions(List RuleAction.of( config.getFunction(), specification.resultFunctionByName(config.getFunction()), - config.getConfig())) + config.getArgs())) .toList(); actions.forEach(this::validateActionConfig); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java index 9daf2811a76..2b642c633e2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java @@ -8,5 +8,5 @@ public class ResultFunctionConfig { String function; - ObjectNode config; + ObjectNode args; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index a5a7d724957..264e3f489e5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -5,7 +5,6 @@ import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; @@ -75,8 +74,7 @@ public RequestSpecification(ObjectMapper mapper, resultFunctions = Map.of( ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), - IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog), - LogATagFunction.NAME, new LogATagFunction(mapper)); + IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); } public SchemaFunction schemaFunctionByName(String name) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 02637aa8e36..72a1a5760d3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -25,12 +25,13 @@ protected FilterBiddersResult filterBidders(Imp imp, final Set removedBidders = new HashSet<>(); final ObjectNode updatedExt = imp.getExt().deepCopy(); + for (String bidder : bidders) { if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { continue; } - updatedExt.remove(bidder); + removeBidder(updatedExt, bidder); removedBidders.add(bidder); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index ff1d7e4e49f..d6372212851 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -21,6 +21,7 @@ import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import org.prebid.server.util.StreamUtil; import org.springframework.util.CollectionUtils; import java.util.ArrayList; @@ -70,7 +71,7 @@ public RuleResult apply(ResultFunctionArguments updateResult = removedBidders.isEmpty() + final UpdateResult updateResult = !removedBidders.isEmpty() ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) : UpdateResult.unaltered(bidRequest); @@ -95,6 +96,14 @@ protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { .orElse(false); } + protected void removeBidder(ObjectNode impExt, String bidder) { + final ObjectNode biddersNode = (ObjectNode) (impExt.get("prebid").get("bidder")); + StreamUtil.asStream(biddersNode.fieldNames()) + .filter(bidder::equalsIgnoreCase) + .findAny() + .ifPresent(biddersNode::remove); + } + @Override public void validateConfig(ObjectNode config) { final FilterBiddersFunctionConfig parsedConfig = parseConfig(config); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java index 86807a18754..30462e2a4a2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -30,7 +30,7 @@ protected FilterBiddersResult filterBidders(Imp imp, for (String bidder : IteratorUtils.asIterable(ext.fieldNames())) { if (!bidders.contains(bidder)) { - updatedExt.remove(bidder); + removeBidder(updatedExt, bidder); removedBidders.add(bidder); } } From 75f4b8dace97b1bc9bc96561b89fdc9f5e653e15 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 11 Jul 2025 21:06:24 +0300 Subject: [PATCH 39/94] Refactorings and analytics tags additions. --- .../rule/engine/core/request/Granularity.java | 15 ++++ .../core/request/RequestMatchingRule.java | 27 +++--- .../request/context/RequestResultContext.java | 3 +- .../request/context/RequestSchemaContext.java | 3 +- .../functions/filter/AnalyticsMapper.java | 85 +++++++++++++++++++ .../filter/ExcludeBiddersFunction.java | 33 +++---- .../filter/FilterBiddersFunction.java | 60 ++++++++----- .../filter/FilterBiddersFunctionConfig.java | 7 +- .../result/functions/filter/FilterUtils.java | 31 +++++++ .../filter/IncludeBiddersFunction.java | 35 ++++---- .../schema/functions/AdUnitCodeFunction.java | 5 +- .../functions/AdUnitCodeInFunction.java | 3 +- .../schema/functions/MediaTypeInFunction.java | 3 +- ...RuleEngineProcessedAuctionRequestHook.java | 3 +- .../auction/model/BidRejectionReason.java | 2 + 15 files changed, 232 insertions(+), 83 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java new file mode 100644 index 00000000000..1fc1debd6ee --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java @@ -0,0 +1,15 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +public sealed interface Granularity { + + final class Request implements Granularity { + private static final Request INSTANCE = new Request(); + + public static Request instance() { + return INSTANCE; + } + } + + record Imp(String impId) implements Granularity { + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index 0547bbde474..a7bc1a95109 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -29,8 +29,6 @@ public class RequestMatchingRule implements Rule { - private static final String GLOBAL_IMP_ID = "*"; - private final Schema schema; private final Set schemaFunctionNames; private final RuleTree> ruleTree; @@ -59,23 +57,23 @@ public RequestMatchingRule(Schema schema, @Override public RuleResult process(BidRequest bidRequest, RequestResultContext context) { return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() - ? processRule(bidRequest, GLOBAL_IMP_ID, context.getAuctionContext()) - : processPerImpRule(bidRequest, context); + ? processRule(bidRequest, Granularity.Request.instance(), context.getAuctionContext()) + : processPerImpRule(bidRequest, context.getAuctionContext()); } - private RuleResult processPerImpRule(BidRequest bidRequest, RequestResultContext context) { + private RuleResult processPerImpRule(BidRequest bidRequest, AuctionContext context) { return bidRequest.getImp().stream().reduce( RuleResult.unaltered(bidRequest), (result, imp) -> result.mergeWith( - processRule( - result.getUpdateResult().getValue(), - imp.getId(), - context.getAuctionContext())), + processRule(result.getUpdateResult().getValue(), new Granularity.Imp(imp.getId()), context)), RuleResult::mergeWith); } - private RuleResult processRule(BidRequest bidRequest, String impId, AuctionContext auctionContext) { - final RequestSchemaContext schemaFunctionContext = RequestSchemaContext.of(bidRequest, impId, datacenter); + private RuleResult processRule(BidRequest bidRequest, + Granularity granularity, + AuctionContext auctionContext) { + + final RequestSchemaContext schemaFunctionContext = RequestSchemaContext.of(bidRequest, granularity, datacenter); final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() @@ -95,7 +93,7 @@ private RuleResult processRule(BidRequest bidRequest, String impId, final InfrastructureArguments infrastructureArguments = InfrastructureArguments.builder() - .context(RequestResultContext.of(auctionContext, impId)) + .context(RequestResultContext.of(auctionContext, granularity)) .schemaFunctionResults(mergeWithSchema(schema, matchers)) .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) .ruleFired(ruleConfig.getCondition()) @@ -107,7 +105,10 @@ private RuleResult processRule(BidRequest bidRequest, String impId, RuleResult.unaltered(bidRequest), (result, action) -> result.mergeWith( action.getFunction().apply( - ResultFunctionArguments.of(bidRequest, action.getConfig(), infrastructureArguments))), + ResultFunctionArguments.of( + result.getUpdateResult().getValue(), + action.getConfig(), + infrastructureArguments))), RuleResult::mergeWith); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java index db4cc153063..6a411efe43b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java @@ -2,11 +2,12 @@ import lombok.Value; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; @Value(staticConstructor = "of") public class RequestResultContext { AuctionContext auctionContext; - String impId; + Granularity granularity; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java index bbbe8aaf1bb..64fdef74634 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java @@ -2,13 +2,14 @@ import com.iab.openrtb.request.BidRequest; import lombok.Value; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; @Value(staticConstructor = "of") public class RequestSchemaContext { BidRequest bidRequest; - String impId; + Granularity granularity; String datacenter; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java new file mode 100644 index 00000000000..a9f4f8be8f8 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -0,0 +1,85 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; +import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.v1.analytics.Result; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; + +public class AnalyticsMapper { + + private static final String ACTIVITY_NAME = "rules-filter"; + private static final String SUCCESS_STATUS = "success"; + + private AnalyticsMapper() { + } + + public static Tags toTags(ObjectMapper mapper, + String functionName, + Granularity granularity, + List seatNonBids, + InfrastructureArguments infrastructureArguments, + FilterBiddersFunctionConfig config) { + + final String analyticsKey = infrastructureArguments.getAnalyticsKey(); + if (StringUtils.isEmpty(analyticsKey)) { + return TagsImpl.of(Collections.emptyList()); + } + + final List removedBidders = seatNonBids.stream() + .map(SeatNonBid::getSeat) + .distinct() + .toList(); + if (CollectionUtils.isEmpty(removedBidders)) { + return TagsImpl.of(Collections.emptyList()); + } + + final List impIds = granularity instanceof Granularity.Request + ? Collections.singletonList("*") + : seatNonBids.stream() + .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) + .map(NonBid::getImpId) + .distinct() + .toList(); + + final BidRejectionReason reason = seatNonBids.stream() + .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) + .map(NonBid::getStatusCode) + .findAny() + .orElse(null); + + final AnalyticsData analyticsData = new AnalyticsData( + analyticsKey, + config.getAnalyticsValue(), + infrastructureArguments.getModelVersion(), + infrastructureArguments.getRuleFired(), + functionName, + removedBidders, + reason); + + final Result result = ResultImpl.of( + SUCCESS_STATUS, mapper.valueToTree(analyticsData), AppliedToImpl.builder().impIds(impIds).build()); + + return TagsImpl.of(Collections.singletonList( + ActivityImpl.of(ACTIVITY_NAME, SUCCESS_STATUS, Collections.singletonList(result)))); + } + + + private record AnalyticsData(String analyticsKey, String analyticsValue, String modelVersion, String ruleFired, + String resultFunction, List biddersRemoved, BidRejectionReason seatNonBid) { + + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 72a1a5760d3..310883bd7eb 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -5,9 +5,10 @@ import com.iab.openrtb.request.Imp; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.util.StreamUtil; -import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; public class ExcludeBiddersFunction extends FilterBiddersFunction { @@ -18,24 +19,18 @@ public ExcludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCat } @Override - protected FilterBiddersResult filterBidders(Imp imp, - Set bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie) { - - final Set removedBidders = new HashSet<>(); - final ObjectNode updatedExt = imp.getExt().deepCopy(); - - for (String bidder : bidders) { - if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { - continue; - } - - removeBidder(updatedExt, bidder); - removedBidders.add(bidder); - } + protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSyncedId, UidsCookie uidsCookie) { + final ObjectNode biddersNode = FilterUtils.bidderNode(imp.getExt()); + + return StreamUtil.asStream(biddersNode.fieldNames()) + .filter(bidder -> FilterUtils.containsIgnoreCase(bidders.stream(), bidder)) + .filter(bidder -> + ifSyncedId == null || ifSyncedId != isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) + .collect(Collectors.toSet()); + } - final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); - return FilterBiddersResult.of(updatedImp, removedBidders); + @Override + protected String name() { + return NAME; } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index d6372212851..87aff741199 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -10,23 +10,21 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; -import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.hooks.v1.analytics.Tags; import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; -import org.prebid.server.util.StreamUtil; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -52,30 +50,32 @@ public RuleResult apply(ResultFunctionArguments activities = new ArrayList<>(); final List updatedImps = new ArrayList<>(); - final Set removedBidders = new HashSet<>(); final List seatNonBid = new ArrayList<>(); for (Imp imp : bidRequest.getImp()) { - if (!impId.equals("*") && !StringUtils.equals(impId, imp.getId())) { + if (granularity instanceof Granularity.Imp && + !StringUtils.equals(((Granularity.Imp) granularity).impId(), imp.getId())) { + updatedImps.add(imp); continue; } final FilterBiddersResult result = filterBidders(imp, config.getBidders(), ifSyncedId, uidsCookie); updatedImps.add(result.getImp()); - removedBidders.addAll(result.getBidders()); seatNonBid.addAll(toSeatNonBid(result, rejectionReason)); } - final UpdateResult updateResult = !removedBidders.isEmpty() + final UpdateResult updateResult = !seatNonBid.isEmpty() ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) : UpdateResult.unaltered(bidRequest); - return RuleResult.of(updateResult, TagsImpl.of(activities), seatNonBid); + final Tags tags = AnalyticsMapper.toTags( + mapper, name(), granularity, seatNonBid, infrastructureArguments, config); + + return RuleResult.of(updateResult, tags, seatNonBid); } private static List toSeatNonBid(FilterBiddersResult filterBiddersResult, BidRejectionReason reason) { @@ -85,10 +85,30 @@ private static List toSeatNonBid(FilterBiddersResult filterBiddersRe .toList(); } - protected abstract FilterBiddersResult filterBidders(Imp imp, - Set bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie); + private FilterBiddersResult filterBidders(Imp imp, + Set bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie) { + + final ObjectNode impExt = imp.getExt(); + final Set removedBidders = biddersToRemove(imp, bidders, ifSyncedId, uidsCookie); + + if (removedBidders.isEmpty()) { + return FilterBiddersResult.of(imp, removedBidders); + } + + final ObjectNode updatedExt = impExt.deepCopy(); + final ObjectNode updatedBiddersNode = FilterUtils.bidderNode(updatedExt); + removedBidders.forEach(updatedBiddersNode::remove); + + final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); + return FilterBiddersResult.of(updatedImp, removedBidders); + } + + protected abstract Set biddersToRemove(Imp imp, + Set bidders, + Boolean ifSyncedId, + UidsCookie uidsCookie); protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { return bidderCatalog.cookieFamilyName(bidder) @@ -96,14 +116,6 @@ protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { .orElse(false); } - protected void removeBidder(ObjectNode impExt, String bidder) { - final ObjectNode biddersNode = (ObjectNode) (impExt.get("prebid").get("bidder")); - StreamUtil.asStream(biddersNode.fieldNames()) - .filter(bidder::equalsIgnoreCase) - .findAny() - .ifPresent(biddersNode::remove); - } - @Override public void validateConfig(ObjectNode config) { final FilterBiddersFunctionConfig parsedConfig = parseConfig(config); @@ -123,4 +135,6 @@ private FilterBiddersFunctionConfig parseConfig(JsonNode config) { throw new ConfigurationValidationException(e.getMessage()); } } + + protected abstract String name(); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index c489b86dcba..780943fe996 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -1,21 +1,26 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Value; +import lombok.extern.jackson.Jacksonized; import org.prebid.server.auction.model.BidRejectionReason; import java.util.Set; @Builder +@Jacksonized @Value(staticConstructor = "of") public class FilterBiddersFunctionConfig { Set bidders; @Builder.Default - BidRejectionReason seatNonBid; + @JsonProperty("seatnonbid") + BidRejectionReason seatNonBid = BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; Boolean ifSyncedId; + @JsonProperty("analyticsValue") String analyticsValue; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java new file mode 100644 index 00000000000..8d2d8eda477 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java @@ -0,0 +1,31 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.util.StreamUtil; + +import java.util.Optional; +import java.util.stream.Stream; + +public class FilterUtils { + + private static final String PREBID = "prebid"; + private static final String BIDDER = "bidder"; + + public FilterUtils() { + } + + public static ObjectNode bidderNode(ObjectNode impExt) { + return Optional.ofNullable(impExt.get(PREBID)) + .filter(JsonNode::isObject) + .map(prebidNode -> (ObjectNode) prebidNode) + .map(prebidNode -> prebidNode.get(BIDDER)) + .filter(JsonNode::isObject) + .map(bidderNode -> (ObjectNode) bidderNode) + .orElseThrow(() -> new IllegalStateException("Impression without ext.prebid.bidder")); + } + + public static boolean containsIgnoreCase(Stream stream, String value) { + return stream.anyMatch(value::equalsIgnoreCase); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java index 30462e2a4a2..27811674748 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -3,12 +3,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Imp; -import org.apache.commons.collections4.IteratorUtils; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.util.StreamUtil; -import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; public class IncludeBiddersFunction extends FilterBiddersFunction { @@ -19,23 +19,18 @@ public IncludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCat } @Override - protected FilterBiddersResult filterBidders(Imp imp, - Set bidders, - Boolean ifSyncedId, - UidsCookie uidsCookie) { - - final Set removedBidders = new HashSet<>(); - final ObjectNode ext = imp.getExt(); - final ObjectNode updatedExt = ext.deepCopy(); - - for (String bidder : IteratorUtils.asIterable(ext.fieldNames())) { - if (!bidders.contains(bidder)) { - removeBidder(updatedExt, bidder); - removedBidders.add(bidder); - } - } - - final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); - return FilterBiddersResult.of(updatedImp, removedBidders); + protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSyncedId, UidsCookie uidsCookie) { + final ObjectNode biddersNode = FilterUtils.bidderNode(imp.getExt()); + + return StreamUtil.asStream(biddersNode.fieldNames()) + .filter(bidder -> !FilterUtils.containsIgnoreCase(bidders.stream(), bidder)) + .filter(bidder -> + ifSyncedId == null || ifSyncedId != isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) + .collect(Collectors.toSet()); + } + + @Override + protected String name() { + return NAME; } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index 624c7eddb9d..582be17ae9a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -4,8 +4,9 @@ import com.iab.openrtb.request.BidRequest; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -17,7 +18,7 @@ public class AdUnitCodeFunction implements SchemaFunction @Override public String extract(SchemaFunctionArguments arguments) { final RequestSchemaContext context = arguments.getOperand(); - final String impId = context.getImpId(); + final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); final BidRequest bidRequest = context.getBidRequest(); return ListUtils.emptyIfNull(bidRequest.getImp()).stream() diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 658bcbf5626..5ef0cab4e9f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -6,6 +6,7 @@ import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; @@ -27,7 +28,7 @@ public class AdUnitCodeInFunction implements SchemaFunction arguments) { final RequestSchemaContext context = arguments.getOperand(); - final String impId = context.getImpId(); + final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId();; final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index df0b29d1bd4..84a2432cf40 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -6,6 +6,7 @@ import com.iab.openrtb.request.Imp; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; @@ -26,7 +27,7 @@ public class MediaTypeInFunction implements SchemaFunction public String extract(SchemaFunctionArguments arguments) { final RequestSchemaContext context = arguments.getOperand(); - final String impId = context.getImpId(); + final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId();; final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 8f577ef81cb..a867cf66266 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -6,6 +6,7 @@ import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; @@ -48,7 +49,7 @@ private static RuleResult executeSafely(Rule Date: Sun, 13 Jul 2025 07:15:26 +0300 Subject: [PATCH 40/94] Enhanced registry cache with versioning and exponential backoff throttling. --- .../config/RuleEngineModuleConfiguration.java | 31 ++-- .../rule/engine/core/cache/RuleRegistry.java | 52 ------- .../core/config/AccountConfigParser.java | 1 + .../engine/core/config/cache/RuleParser.java | 140 ++++++++++++++++++ .../core/config/model/AccountConfig.java | 3 +- .../rule/engine/core/rules/PerStageRule.java | 2 + .../rule/engine/v1/RuleEngineModule.java | 7 +- ...RuleEngineProcessedAuctionRequestHook.java | 10 +- 8 files changed, 174 insertions(+), 72 deletions(-) delete mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index cb73a75bcba..daa08398825 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -3,14 +3,15 @@ import com.iab.openrtb.request.BidRequest; import io.vertx.core.Vertx; import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.execution.retry.ExponentialBackoffRetryPolicy; import org.prebid.server.hooks.execution.model.Stage; -import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.beans.factory.annotation.Value; @@ -18,6 +19,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.time.Clock; import java.util.concurrent.ThreadLocalRandom; @Configuration @@ -25,8 +27,8 @@ public class RuleEngineModuleConfiguration { @Bean - RuleEngineModule ruleEngineModule(RuleRegistry ruleRegistry) { - return new RuleEngineModule(ruleRegistry); + RuleEngineModule ruleEngineModule(RuleParser ruleParser) { + return new RuleEngineModule(ruleParser); } @Bean @@ -51,12 +53,23 @@ AccountConfigParser accountConfigParser( } @Bean - RuleRegistry ruleRegistry( - @Value("${hooks.rule-engine.rule-cache-expire-after-minutes}") long cacheExpireAfterMinutes, - @Value("${hooks.rule-engine.rule-cache-max-size}") long cacheMaxSize, + RuleParser ruleParser( + @Value("${hooks.rule-engine.rule-cache.expire-after-minutes}") long cacheExpireAfterMinutes, + @Value("${hooks.rule-engine.rule-cache.max-size}") long cacheMaxSize, + @Value("${hooks.rule-engine.rule-parsing.retry-initial-delay-millis}") long delay, + @Value("${hooks.rule-engine.rule-parsing.retry-max-delay-millis}") long maxDelay, + @Value("${hooks.rule-engine.rule-parsing.retry-exponential-factor}") double factor, + @Value("${hooks.rule-engine.rule-parsing.retry-exponential-jitter}") double jitter, AccountConfigParser accountConfigParser, - Vertx vertx) { + Vertx vertx, + Clock clock) { - return new RuleRegistry(cacheExpireAfterMinutes, cacheMaxSize, accountConfigParser, vertx); + return new RuleParser( + cacheExpireAfterMinutes, + cacheMaxSize, + ExponentialBackoffRetryPolicy.of(delay, maxDelay, factor, jitter), + accountConfigParser, + vertx, + clock); } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java deleted file mode 100644 index 2290d589cb9..00000000000 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/cache/RuleRegistry.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.cache; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.benmanes.caffeine.cache.Caffeine; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; - -import java.util.Objects; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -public class RuleRegistry { - - private final AccountConfigParser parser; - private final Vertx vertx; - - private final ConcurrentMap> accountIdToRules; - - public RuleRegistry(long cacheExpireAfterMinutes, - long cacheMaxSize, - AccountConfigParser parser, - Vertx vertx) { - - this.parser = Objects.requireNonNull(parser); - this.vertx = Objects.requireNonNull(vertx); - - this.accountIdToRules = Caffeine.newBuilder() - .expireAfterAccess(cacheExpireAfterMinutes, TimeUnit.MINUTES) - .maximumSize(cacheMaxSize) - .>build() - .asMap(); - } - - public Future forAccount(String accountId, ObjectNode config) { - // TODO: think about adding exponential backoff for account with invalid config, - // since parsing is heavy operation - - // TODO: utilize timestamp for cache invalidation - return accountIdToRules.computeIfAbsent( - accountId, (ignored) -> vertx.executeBlocking(() -> parser.parse(config))) - .recover(error -> evictCacheAndRethrowError(accountId, error)); - } - - private Future evictCacheAndRethrowError(String accountId, Throwable error) { - accountIdToRules.compute( - accountId, (ignored, ruleFuture) -> ruleFuture == null || ruleFuture.failed() ? null : ruleFuture); - - return Future.failedFuture(error); - } -} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index 59b7a06ece9..d637ecc2193 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -36,6 +36,7 @@ public PerStageRule parse(ObjectNode accountConfig) { } return PerStageRule.builder() + .version(parsedConfig.getTimestamp()) .processedAuctionRequestRule(processedAuctionRequestStageParser.parse(parsedConfig)) .build(); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java new file mode 100644 index 00000000000..7d9504b4189 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java @@ -0,0 +1,140 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config.cache; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.benmanes.caffeine.cache.Caffeine; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import lombok.experimental.Accessors; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.execution.retry.RetryPolicy; +import org.prebid.server.execution.retry.Retryable; +import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class RuleParser { + + private static final Logger logger = LoggerFactory.getLogger(RuleParser.class); + + private final AccountConfigParser parser; + private final Vertx vertx; + private final Clock clock; + + private final RetryPolicy retryPolicy; + + private final Map accountIdToParsingAttempt; + private final Map accountIdToRules; + + + public RuleParser(long cacheExpireAfterMinutes, + long cacheMaxSize, + RetryPolicy retryPolicy, + AccountConfigParser parser, + Vertx vertx, + Clock clock) { + + this.parser = Objects.requireNonNull(parser); + this.vertx = Objects.requireNonNull(vertx); + this.clock = Objects.requireNonNull(clock); + this.retryPolicy = Objects.requireNonNull(retryPolicy); + + // TODO: Tune exposed properties for cache control, switch hashmap to caffeine + this.accountIdToParsingAttempt = new ConcurrentHashMap<>(); + this.accountIdToRules = Caffeine.newBuilder() + .expireAfterAccess(cacheExpireAfterMinutes, TimeUnit.MINUTES) + .maximumSize(cacheMaxSize) + .build() + .asMap(); + } + + public Future parseForAccount(String accountId, ObjectNode config) { + final PerStageRule cachedRule = accountIdToRules.get(accountId); + + if (cachedRule != null && cachedRule.version() >= getConfigVersion(config)) { + return Future.succeededFuture(cachedRule); + } + + parseConfig(accountId, config); + return cachedRule == null + ? Future.failedFuture(new PreBidException("Rule for account " + accountId + " is not ready")) + : Future.succeededFuture(cachedRule); + } + + private long getConfigVersion(ObjectNode config) { + return config.path("timestamp").asLong(); + } + + private void parseConfig(String accountId, ObjectNode config) { + final Instant now = clock.instant(); + final ParsingAttempt attempt = accountIdToParsingAttempt.compute( + accountId, (ignored, previousAttempt) -> tryRegisteringNewAttempt(previousAttempt, now)); + + // reference equality used on purpose - if references are equal - then we should parse + if (attempt.timestamp() == now) { + logger.info("Parsing rule for account {}", accountId); + vertx.executeBlocking(() -> parser.parse(config)) + .onSuccess(result -> succeedParsingAttempt(accountId, result)) + .onFailure(error -> failParsingAttempt(accountId, attempt, error)); + } + } + + private ParsingAttempt tryRegisteringNewAttempt(ParsingAttempt previousAttempt, Instant currentAttemptStart) { + if (previousAttempt == null) { + return new ParsingAttempt.InProgress(currentAttemptStart, retryPolicy); + } + + if (previousAttempt instanceof ParsingAttempt.InProgress) { + return previousAttempt; + } + + if (previousAttempt.retryPolicy() instanceof Retryable previousAttemptRetryPolicy) { + final Instant previouslyDecidedToRetryAfter = previousAttempt.timestamp().plus( + Duration.ofMillis(previousAttemptRetryPolicy.delay())); + + return previouslyDecidedToRetryAfter.isBefore(currentAttemptStart) + ? new ParsingAttempt.InProgress(currentAttemptStart, previousAttemptRetryPolicy.next()) + : previousAttempt; + } + + return previousAttempt; + } + + private void succeedParsingAttempt(String accountId, PerStageRule result) { + accountIdToRules.put(accountId, result); + accountIdToParsingAttempt.remove(accountId); + } + + private void failParsingAttempt(String accountId, ParsingAttempt attempt, Throwable cause) { + accountIdToParsingAttempt.put(accountId, ((ParsingAttempt.InProgress) attempt).failed()); + + logger.error("Failed to parse rules config for account %s: %s".formatted(accountId, cause.getMessage()), cause); + } + + private sealed interface ParsingAttempt { + + Instant timestamp(); + + RetryPolicy retryPolicy(); + + @Accessors(fluent = true) + record Failed(Instant timestamp, RetryPolicy retryPolicy) implements ParsingAttempt { + } + + @Accessors(fluent = true) + record InProgress(Instant timestamp, RetryPolicy retryPolicy) implements ParsingAttempt { + + public Failed failed() { + return new Failed(timestamp, retryPolicy); + } + } + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java index 5ea5ef8cc1e..10c2ba8cb4f 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Value; -import java.time.Instant; import java.util.List; @Value(staticConstructor = "of") @@ -11,7 +10,7 @@ public class AccountConfig { boolean enabled; - Instant timestamp; + long timestamp; @JsonProperty("ruleSets") List ruleSets; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index ca94640135b..432d0d6265a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -11,6 +11,8 @@ @Value(staticConstructor = "of") public class PerStageRule { + long version; + Rule processedAuctionRequestRule; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java index fe41fc1ee28..99708279913 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java @@ -1,7 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.v1; -import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; -import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; import org.prebid.server.hooks.v1.Hook; import org.prebid.server.hooks.v1.InvocationContext; import org.prebid.server.hooks.v1.Module; @@ -15,9 +14,9 @@ public class RuleEngineModule implements Module { private final Collection> hooks; - public RuleEngineModule(RuleRegistry ruleRegistry) { + public RuleEngineModule(RuleParser ruleParser) { this.hooks = Collections.singleton( - new RuleEngineProcessedAuctionRequestHook(ruleRegistry)); + new RuleEngineProcessedAuctionRequestHook(ruleParser)); } @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index a867cf66266..9fd98d10fdf 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -5,7 +5,7 @@ import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.cache.RuleRegistry; +import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; @@ -25,10 +25,10 @@ public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRe private static final String CODE = "rule-engine-processed-auction-request"; - private final RuleRegistry ruleRegistry; + private final RuleParser ruleParser; - public RuleEngineProcessedAuctionRequestHook(RuleRegistry ruleRegistry) { - this.ruleRegistry = Objects.requireNonNull(ruleRegistry); + public RuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { + this.ruleParser = Objects.requireNonNull(ruleParser); } @Override @@ -37,7 +37,7 @@ public Future> call(AuctionRequestPayloa final AuctionContext context = invocationContext.auctionContext(); final String accountId = invocationContext.auctionContext().getAccount().getId(); - return ruleRegistry.forAccount(accountId, invocationContext.accountConfig()) + return ruleParser.parseForAccount(accountId, invocationContext.accountConfig()) .map(PerStageRule::processedAuctionRequestRule) .map(rule -> executeSafely(rule, auctionRequestPayload.bidRequest(), context)) .map(RuleEngineProcessedAuctionRequestHook::succeeded) From f0d1b2b13d3b17d004c7a9607ed7f842bdb2e26f Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 14 Jul 2025 17:19:19 +0300 Subject: [PATCH 41/94] Fixed error in exclude/include result functions related to ifSyncedId handling. --- .../request/result/functions/filter/ExcludeBiddersFunction.java | 2 +- .../request/result/functions/filter/IncludeBiddersFunction.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 310883bd7eb..52d2a0c479a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -25,7 +25,7 @@ protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSy return StreamUtil.asStream(biddersNode.fieldNames()) .filter(bidder -> FilterUtils.containsIgnoreCase(bidders.stream(), bidder)) .filter(bidder -> - ifSyncedId == null || ifSyncedId != isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) + ifSyncedId == null || ifSyncedId == isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) .collect(Collectors.toSet()); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java index 27811674748..38a08e2ecb1 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -25,7 +25,7 @@ protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSy return StreamUtil.asStream(biddersNode.fieldNames()) .filter(bidder -> !FilterUtils.containsIgnoreCase(bidders.stream(), bidder)) .filter(bidder -> - ifSyncedId == null || ifSyncedId != isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) + ifSyncedId == null || ifSyncedId == isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) .collect(Collectors.toSet()); } From 3b837dc9d2e65afe0d6fde7106c56f2d563e2367 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 02:12:45 +0300 Subject: [PATCH 42/94] Added toggles on different levels, secured code against NPEs. --- .../config/RuleEngineModuleConfiguration.java | 2 +- .../core/config/AccountConfigParser.java | 10 +++- .../core/config/{cache => }/RuleParser.java | 25 +++++++--- .../engine/core/config/StageConfigParser.java | 9 ++-- .../core/config/model/AccountConfig.java | 11 ++++- .../core/config/model/RuleSetConfig.java | 6 +++ .../core/rules/AlternativeActionRule.java | 11 ++--- .../rule/engine/core/rules/NoOpRule.java | 4 +- .../rule/engine/core/rules/PerStageRule.java | 4 +- .../rule/engine/v1/RuleEngineModule.java | 2 +- ...RuleEngineProcessedAuctionRequestHook.java | 47 ++++++++++--------- 11 files changed, 85 insertions(+), 46 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/{cache => }/RuleParser.java (84%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index daa08398825..dabcb92f9a8 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -6,8 +6,8 @@ import org.prebid.server.execution.retry.ExponentialBackoffRetryPolicy; import org.prebid.server.hooks.execution.model.Stage; import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRule; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index d637ecc2193..149e7b2ac4c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -8,6 +8,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import java.util.Objects; @@ -35,8 +36,15 @@ public PerStageRule parse(ObjectNode accountConfig) { throw new PreBidException(e.getMessage()); } + if (!parsedConfig.isEnabled()) { + return PerStageRule.builder() + .timestamp(parsedConfig.getTimestamp()) + .processedAuctionRequestRule(NoOpRule.create()) + .build(); + } + return PerStageRule.builder() - .version(parsedConfig.getTimestamp()) + .timestamp(parsedConfig.getTimestamp()) .processedAuctionRequestRule(processedAuctionRequestStageParser.parse(parsedConfig)) .build(); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java similarity index 84% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java index 7d9504b4189..10e49fea5ef 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/cache/RuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java @@ -1,5 +1,6 @@ -package org.prebid.server.hooks.modules.rule.engine.core.config.cache; +package org.prebid.server.hooks.modules.rule.engine.core.config; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.benmanes.caffeine.cache.Caffeine; import io.vertx.core.Future; @@ -8,7 +9,6 @@ import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.retry.RetryPolicy; import org.prebid.server.execution.retry.Retryable; -import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; @@ -16,8 +16,10 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.time.format.DateTimeParseException; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -59,7 +61,7 @@ public RuleParser(long cacheExpireAfterMinutes, public Future parseForAccount(String accountId, ObjectNode config) { final PerStageRule cachedRule = accountIdToRules.get(accountId); - if (cachedRule != null && cachedRule.version() >= getConfigVersion(config)) { + if (cachedRule != null && cachedRule.timestamp().compareTo(getConfigTimestamp(config)) >= 0) { return Future.succeededFuture(cachedRule); } @@ -69,8 +71,17 @@ public Future parseForAccount(String accountId, ObjectNode config) : Future.succeededFuture(cachedRule); } - private long getConfigVersion(ObjectNode config) { - return config.path("timestamp").asLong(); + private Instant getConfigTimestamp(ObjectNode config) { + try { + return Optional.of(config) + .map(node -> node.get("timestamp")) + .filter(JsonNode::isTextual) + .map(JsonNode::asText) + .map(Instant::parse) + .orElse(Instant.EPOCH); + } catch (DateTimeParseException exception) { + return Instant.EPOCH; + } } private void parseConfig(String accountId, ObjectNode config) { @@ -116,7 +127,9 @@ private void succeedParsingAttempt(String accountId, PerStageRule result) { private void failParsingAttempt(String accountId, ParsingAttempt attempt, Throwable cause) { accountIdToParsingAttempt.put(accountId, ((ParsingAttempt.InProgress) attempt).failed()); - logger.error("Failed to parse rules config for account %s: %s".formatted(accountId, cause.getMessage()), cause); + logger.error( + "Failed to parse rule-engine config for account %s: %s".formatted(accountId, cause.getMessage()), + cause); } private sealed interface ParsingAttempt { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 83380947b84..33f1cd94452 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -52,11 +52,14 @@ public StageConfigParser(RandomGenerator randomGenerator, public Rule parse(AccountConfig config) { final List> stageSubrules = config.getRuleSets().stream() .filter(ruleSet -> stage.equals(ruleSet.getStage())) + .filter(RuleSetConfig::isEnabled) .map(RuleSetConfig::getModelGroups) .map(this::parseModelGroupConfigs) .toList(); - return CompositeRule.of(stageSubrules); + return stageSubrules.isEmpty() + ? NoOpRule.create() + : CompositeRule.of(stageSubrules); } private Rule parseModelGroupConfigs(List modelGroupConfigs) { @@ -162,9 +165,9 @@ private Rule combineRules( Rule left, Rule right) { if (left == null && right == null) { - return new NoOpRule<>(); + return NoOpRule.create(); } else if (left != null && right != null) { - return new AlternativeActionRule<>(left, right); + return AlternativeActionRule.of(left, right); } else if (left != null) { return left; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java index 10c2ba8cb4f..d005d7f1d03 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java @@ -1,16 +1,23 @@ package org.prebid.server.hooks.modules.rule.engine.core.config.model; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Value; +import lombok.extern.jackson.Jacksonized; +import java.time.Instant; import java.util.List; +@Builder +@Jacksonized @Value(staticConstructor = "of") public class AccountConfig { - boolean enabled; + @Builder.Default + boolean enabled = true; - long timestamp; + @Builder.Default + Instant timestamp = Instant.EPOCH; @JsonProperty("ruleSets") List ruleSets; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java index e8aa507182c..c845403f49c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java @@ -1,14 +1,20 @@ package org.prebid.server.hooks.modules.rule.engine.core.config.model; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Value; +import lombok.extern.jackson.Jacksonized; import org.prebid.server.hooks.execution.model.Stage; import java.util.List; +@Builder +@Jacksonized @Value(staticConstructor = "of") public class RuleSetConfig { + boolean enabled = true; + Stage stage; String name; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java index 365ef98e6af..4370ef58502 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java @@ -1,16 +1,13 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +import lombok.Value; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; +@Value(staticConstructor = "of") public class AlternativeActionRule implements Rule { - private final Rule delegate; - private final Rule alternative; - - public AlternativeActionRule(Rule delegate, Rule alternative) { - this.delegate = delegate; - this.alternative = alternative; - } + Rule delegate; + Rule alternative; public RuleResult process(T value, C context) { try { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java index 6be471ddfcd..e09170d544c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -1,12 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; -import lombok.NoArgsConstructor; +import lombok.Value; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.model.UpdateResult; import java.util.Collections; -@NoArgsConstructor +@Value(staticConstructor = "create") public class NoOpRule implements Rule { @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index 432d0d6265a..63a0a0e9418 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -6,12 +6,14 @@ import lombok.experimental.Accessors; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import java.time.Instant; + @Builder @Accessors(fluent = true) @Value(staticConstructor = "of") public class PerStageRule { - long version; + Instant timestamp; Rule processedAuctionRequestRule; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java index 99708279913..ad861eaba63 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.v1; -import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.v1.Hook; import org.prebid.server.hooks.v1.InvocationContext; import org.prebid.server.hooks.v1.Module; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java index 9fd98d10fdf..0e0271ee041 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java @@ -1,15 +1,16 @@ package org.prebid.server.hooks.modules.rule.engine.v1; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import io.vertx.core.Future; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; -import org.prebid.server.hooks.modules.rule.engine.core.config.cache.RuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; @@ -36,32 +37,34 @@ public Future> call(AuctionRequestPayloa AuctionInvocationContext invocationContext) { final AuctionContext context = invocationContext.auctionContext(); - final String accountId = invocationContext.auctionContext().getAccount().getId(); - return ruleParser.parseForAccount(accountId, invocationContext.accountConfig()) - .map(PerStageRule::processedAuctionRequestRule) - .map(rule -> executeSafely(rule, auctionRequestPayload.bidRequest(), context)) - .map(RuleEngineProcessedAuctionRequestHook::succeeded) - .recover(RuleEngineProcessedAuctionRequestHook::failure); - } + final String accountId = StringUtils.defaultString(invocationContext.auctionContext().getAccount().getId()); + final ObjectNode accountConfig = invocationContext.accountConfig(); + final BidRequest bidRequest = auctionRequestPayload.bidRequest(); - private static RuleResult executeSafely(Rule rule, - BidRequest bidRequest, - AuctionContext context) { + if (accountConfig == null) { + return succeeded(RuleResult.unaltered(bidRequest)); + } - return rule != null - ? rule.process(bidRequest, RequestResultContext.of(context, Granularity.Request.instance())) - : RuleResult.unaltered(bidRequest); + return ruleParser.parseForAccount(accountId, accountConfig) + .map(PerStageRule::processedAuctionRequestRule) + .map(rule -> rule.process( + bidRequest, RequestResultContext.of(context, Granularity.Request.instance()))) + .flatMap(RuleEngineProcessedAuctionRequestHook::succeeded) + .recover(RuleEngineProcessedAuctionRequestHook::failure); } - private static InvocationResult succeeded(RuleResult result) { + private static Future> succeeded(RuleResult result) { final UpdateResult updateResult = result.getUpdateResult(); - return InvocationResultImpl.builder() - .status(InvocationStatus.success) - .action(updateResult.isUpdated() ? InvocationAction.update : InvocationAction.no_action) - .payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(updateResult.getValue())) - .analyticsTags(result.getAnalyticsTags()) - .build(); + final InvocationResult invocationResult = + InvocationResultImpl.builder() + .status(InvocationStatus.success) + .action(updateResult.isUpdated() ? InvocationAction.update : InvocationAction.no_action) + .payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(updateResult.getValue())) + .analyticsTags(result.getAnalyticsTags()) + .build(); + + return Future.succeededFuture(invocationResult); } private static Future> failure(Throwable error) { From 19f71c58f32b7ebd864b19b1990f7641e861c7bb Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 02:34:50 +0300 Subject: [PATCH 43/94] Added LogATag result function. --- .../core/request/RequestSpecification.java | 4 +- .../functions/filter/AnalyticsMapper.java | 20 +++---- .../filter/FilterBiddersFunction.java | 2 +- .../result/functions/log/AnalyticsMapper.java | 59 +++++++++++++++++++ .../result/functions/log/LogATagFunction.java | 43 ++++++++++++++ 5 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 264e3f489e5..7cf60870248 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -5,6 +5,7 @@ import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; @@ -74,7 +75,8 @@ public RequestSpecification(ObjectMapper mapper, resultFunctions = Map.of( ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), - IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); + IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog), + LogATagFunction.NAME, new LogATagFunction(mapper)); } public SchemaFunction schemaFunctionByName(String name) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java index a9f4f8be8f8..6e6c3b844ac 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -29,10 +29,9 @@ private AnalyticsMapper() { public static Tags toTags(ObjectMapper mapper, String functionName, - Granularity granularity, List seatNonBids, InfrastructureArguments infrastructureArguments, - FilterBiddersFunctionConfig config) { + String analyticsValue) { final String analyticsKey = infrastructureArguments.getAnalyticsKey(); if (StringUtils.isEmpty(analyticsKey)) { @@ -47,13 +46,14 @@ public static Tags toTags(ObjectMapper mapper, return TagsImpl.of(Collections.emptyList()); } - final List impIds = granularity instanceof Granularity.Request - ? Collections.singletonList("*") - : seatNonBids.stream() - .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) - .map(NonBid::getImpId) - .distinct() - .toList(); + final List impIds = + infrastructureArguments.getContext().getGranularity() instanceof Granularity.Request + ? Collections.singletonList("*") + : seatNonBids.stream() + .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) + .map(NonBid::getImpId) + .distinct() + .toList(); final BidRejectionReason reason = seatNonBids.stream() .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) @@ -63,7 +63,7 @@ public static Tags toTags(ObjectMapper mapper, final AnalyticsData analyticsData = new AnalyticsData( analyticsKey, - config.getAnalyticsValue(), + analyticsValue, infrastructureArguments.getModelVersion(), infrastructureArguments.getRuleFired(), functionName, diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 87aff741199..42ddcc477d9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -73,7 +73,7 @@ public RuleResult apply(ResultFunctionArguments infrastructureArguments, + String analyticsValue) { + + final String analyticsKey = infrastructureArguments.getAnalyticsKey(); + if (StringUtils.isEmpty(analyticsKey)) { + return TagsImpl.of(Collections.emptyList()); + } + + final AnalyticsData analyticsData = new AnalyticsData( + analyticsKey, + analyticsValue, + infrastructureArguments.getModelVersion(), + infrastructureArguments.getRuleFired(), + LogATagFunction.NAME); + + final Granularity granularity = infrastructureArguments.getContext().getGranularity(); + final List impIds = granularity instanceof Granularity.Imp + ? Collections.singletonList(((Granularity.Imp) granularity).impId()) + : Collections.singletonList("*"); + + final Result result = ResultImpl.of( + SUCCESS_STATUS, mapper.valueToTree(analyticsData), AppliedToImpl.builder().impIds(impIds).build()); + + return TagsImpl.of(Collections.singletonList( + ActivityImpl.of(ACTIVITY_NAME, SUCCESS_STATUS, Collections.singletonList(result)))); + } + + + private record AnalyticsData(String analyticsKey, String analyticsValue, String modelVersion, String ruleFired, + String resultFunction) { + + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java new file mode 100644 index 00000000000..d2f40df2294 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java @@ -0,0 +1,43 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; + +import java.util.Collections; +import java.util.Objects; + +public class LogATagFunction implements ResultFunction { + + public static final String NAME = "logAtag"; + + private static final String ANALYTICS_VALUE_FIELD = "analyticsValue"; + + private final ObjectMapper mapper; + + public LogATagFunction(ObjectMapper mapper) { + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public RuleResult apply(ResultFunctionArguments arguments) { + final Tags tags = AnalyticsMapper.toTags( + mapper, + arguments.getInfrastructureArguments(), + arguments.getConfig().get(ANALYTICS_VALUE_FIELD).asText()); + + return RuleResult.of(UpdateResult.unaltered(arguments.getOperand()), tags, Collections.emptyList()); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertString(config, ANALYTICS_VALUE_FIELD); + } +} From 8fb7db0d625c25ab46d3d9563b9e37584149abf8 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 02:48:31 +0300 Subject: [PATCH 44/94] Changed parser cache backend. --- .../modules/rule/engine/core/config/RuleParser.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java index 10e49fea5ef..c027ee214a3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java @@ -20,7 +20,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; public class RuleParser { @@ -36,7 +35,6 @@ public class RuleParser { private final Map accountIdToParsingAttempt; private final Map accountIdToRules; - public RuleParser(long cacheExpireAfterMinutes, long cacheMaxSize, RetryPolicy retryPolicy, @@ -49,8 +47,12 @@ public RuleParser(long cacheExpireAfterMinutes, this.clock = Objects.requireNonNull(clock); this.retryPolicy = Objects.requireNonNull(retryPolicy); - // TODO: Tune exposed properties for cache control, switch hashmap to caffeine - this.accountIdToParsingAttempt = new ConcurrentHashMap<>(); + this.accountIdToParsingAttempt = Caffeine.newBuilder() + .expireAfterAccess(cacheExpireAfterMinutes, TimeUnit.MINUTES) + .maximumSize(cacheMaxSize) + .build() + .asMap(); + this.accountIdToRules = Caffeine.newBuilder() .expireAfterAccess(cacheExpireAfterMinutes, TimeUnit.MINUTES) .maximumSize(cacheMaxSize) From c1f2b918da6bb6574195d9a230ba251e46ac423f Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 02:51:42 +0300 Subject: [PATCH 45/94] Refactorings. --- .../core/request/schema/functions/AdUnitCodeFunction.java | 2 +- .../core/request/schema/functions/AdUnitCodeInFunction.java | 5 +++-- .../request/{ => schema/functions}/util/AdUnitCodeUtils.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) rename extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{ => schema/functions}/util/AdUnitCodeUtils.java (98%) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index 582be17ae9a..37a12a4c85e 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -6,7 +6,7 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 5ef0cab4e9f..8fb99011b4a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -7,8 +7,8 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -28,7 +28,8 @@ public class AdUnitCodeInFunction implements SchemaFunction arguments) { final RequestSchemaContext context = arguments.getOperand(); - final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId();; + final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); + ; final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java similarity index 98% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java rename to extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java index 11c3566c1e2..1d7c8093fa6 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/util/AdUnitCodeUtils.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java @@ -1,4 +1,4 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.util; +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util; import com.fasterxml.jackson.databind.JsonNode; import com.iab.openrtb.request.Imp; From 67cc1a51ceba62fd05c2ea94bfd4e568a1e838df Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 10:41:41 +0300 Subject: [PATCH 46/94] Finished all schema functions. --- .../config/RuleEngineModuleConfiguration.java | 7 ++-- .../core/request/RequestSpecification.java | 12 +++++-- .../schema/functions/DomainFunction.java | 7 ++-- .../schema/functions/DomainInFunction.java | 34 +++++++++++++++++++ .../schema/functions/PrebidKeyFunction.java | 18 ++++++---- .../schema/functions/util/DomainUtils.java | 32 +++++++++++++++++ .../schema/functions/PercentFunction.java | 4 +-- .../openrtb/ext/request/ExtRequestPrebid.java | 2 ++ 8 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java create mode 100644 extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index dabcb92f9a8..f25cc1ff49a 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -21,6 +21,7 @@ import java.time.Clock; import java.util.concurrent.ThreadLocalRandom; +import java.util.random.RandomGenerator; @Configuration @ConditionalOnProperty(prefix = "hooks." + RuleEngineModule.CODE, name = "enabled", havingValue = "true") @@ -36,10 +37,12 @@ StageConfigParser proces BidderCatalog bidderCatalog, @Value("${datacenter-region:#{null}}") String datacenterRegion) { + final RandomGenerator randomGenerator = () -> ThreadLocalRandom.current().nextLong(); + return new StageConfigParser<>( - () -> ThreadLocalRandom.current().nextLong(), + randomGenerator, Stage.processed_auction_request, - new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog), + new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), (schema, ruleTree, modelVersion, analyticsKey) -> new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 7cf60870248..af22172e26c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -5,9 +5,9 @@ import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.BundleFunction; @@ -20,6 +20,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceTypeFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DeviceTypeInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DomainFunction; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.DomainInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.EidAvailableFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.EidInFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.FpdAvailableFunction; @@ -33,10 +34,12 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.functions.PercentFunction; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.random.RandomGenerator; public class RequestSpecification implements StageSpecification { @@ -48,7 +51,8 @@ public class RequestSpecification implements private final Map> resultFunctions; public RequestSpecification(ObjectMapper mapper, - BidderCatalog bidderCatalog) { + BidderCatalog bidderCatalog, + RandomGenerator random) { schemaFunctions = new HashMap<>(); schemaFunctions.put(AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); @@ -63,19 +67,21 @@ public RequestSpecification(ObjectMapper mapper, schemaFunctions.put(DeviceTypeFunction.NAME, new DeviceTypeFunction()); schemaFunctions.put(DeviceTypeInFunction.NAME, new DeviceTypeInFunction()); schemaFunctions.put(DomainFunction.NAME, new DomainFunction()); + schemaFunctions.put(DomainInFunction.NAME, new DomainInFunction()); schemaFunctions.put(EidAvailableFunction.NAME, new EidAvailableFunction()); schemaFunctions.put(EidInFunction.NAME, new EidInFunction()); schemaFunctions.put(FpdAvailableFunction.NAME, new FpdAvailableFunction()); schemaFunctions.put(GppSidAvailableFunction.NAME, new GppSidAvailableFunction()); schemaFunctions.put(GppSidInFunction.NAME, new GppSidInFunction()); schemaFunctions.put(MediaTypeInFunction.NAME, new MediaTypeInFunction()); + schemaFunctions.put(PercentFunction.NAME, new PercentFunction<>(random)); schemaFunctions.put(PrebidKeyFunction.NAME, new PrebidKeyFunction()); schemaFunctions.put(TcfInScopeFunction.NAME, new TcfInScopeFunction()); schemaFunctions.put(UserFpdAvailableFunction.NAME, new UserFpdAvailableFunction()); resultFunctions = Map.of( - ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog), + ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), LogATagFunction.NAME, new LogATagFunction(mapper)); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java index 5672aad51fa..fe8943febee 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java @@ -1,8 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.iab.openrtb.request.BidRequest; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.DomainUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -13,9 +13,8 @@ public class DomainFunction implements SchemaFunction { @Override public String extract(SchemaFunctionArguments arguments) { - final BidRequest bidRequest = arguments.getOperand().getBidRequest(); - - return UNDEFINED_RESULT; + return DomainUtils.extractDomain(arguments.getOperand().getBidRequest()) + .orElse(UNDEFINED_RESULT); } @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java new file mode 100644 index 00000000000..dd51db9e673 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java @@ -0,0 +1,34 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.DomainUtils; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.util.StreamUtil; + +public class DomainInFunction implements SchemaFunction { + + public static final String NAME = "domainIn"; + + private static final String DOMAINS_FIELD = "domains"; + + @Override + public String extract(SchemaFunctionArguments arguments) { + final String domain = DomainUtils.extractDomain(arguments.getOperand().getBidRequest()) + .orElse(UNDEFINED_RESULT); + + final boolean matches = StreamUtil.asStream(arguments.getConfig().get(DOMAINS_FIELD).elements()) + .map(JsonNode::asText) + .anyMatch(domain::equals); + + return Boolean.toString(matches); + } + + @Override + public void validateConfig(ObjectNode config) { + ValidationUtils.assertArrayOfStrings(config, DOMAINS_FIELD); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java index 9e71e30eb43..bb16f92c186 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java @@ -1,10 +1,15 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; + +import java.util.Optional; public class PrebidKeyFunction implements SchemaFunction { @@ -14,12 +19,13 @@ public class PrebidKeyFunction implements SchemaFunction { @Override public String extract(SchemaFunctionArguments arguments) { -// final String key = arguments.getConfig().get(KEY_FIELD).asText(); -// return Optional.ofNullable(arguments.getOperand().getBidRequest().getExt()) -// .map(ext -> ext.getPrebid()) -// .map(e -> e.get) - - return UNDEFINED_RESULT; + return Optional.ofNullable(arguments.getOperand().getBidRequest().getExt()) + .map(ExtRequest::getPrebid) + .map(ExtRequestPrebid::getKvps) + .map(kvps -> kvps.get(arguments.getConfig().get(KEY_FIELD).asText())) + .filter(JsonNode::isTextual) + .map(JsonNode::asText) + .orElse(UNDEFINED_RESULT); } @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java new file mode 100644 index 00000000000..9da8c7b1032 --- /dev/null +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java @@ -0,0 +1,32 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util; + +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Dooh; +import com.iab.openrtb.request.Publisher; +import com.iab.openrtb.request.Site; + +import java.util.Optional; + +public class DomainUtils { + + private DomainUtils() { + } + + public static Optional extractDomain(BidRequest bidRequest) { + return extractPublisherDomain(bidRequest) + .or(() -> extractPlainDomain(bidRequest)); + } + + private static Optional extractPublisherDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()).map(Site::getPublisher).map(Publisher::getDomain) + .or(() -> Optional.ofNullable(bidRequest.getApp()).map(App::getPublisher).map(Publisher::getDomain)) + .or(() -> Optional.ofNullable(bidRequest.getDooh()).map(Dooh::getPublisher).map(Publisher::getDomain)); + } + + private static Optional extractPlainDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()).map(Site::getDomain) + .or(() -> Optional.ofNullable(bidRequest.getApp()).map(App::getDomain)) + .or(() -> Optional.ofNullable(bidRequest.getDooh()).map(Dooh::getDomain)); + } +} diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java index 580e8941b31..abf6163b5d0 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java @@ -9,7 +9,7 @@ import java.util.random.RandomGenerator; @RequiredArgsConstructor -public class PercentFunction implements SchemaFunction { +public class PercentFunction implements SchemaFunction { public static final String NAME = "percent"; @@ -18,7 +18,7 @@ public class PercentFunction implements SchemaFunction { private final RandomGenerator random; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final int resolvedUpperBound = Math.min(Math.max(arguments.getConfig().get(PCT_FIELD).asInt(), 0), 100); return Boolean.toString(random.nextInt(100) < resolvedUpperBound); } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java index 98754a459a5..ace433f1578 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java @@ -193,4 +193,6 @@ public class ExtRequestPrebid { @JsonProperty("alternatebiddercodes") ExtRequestPrebidAlternateBidderCodes alternateBidderCodes; + + ObjectNode kvps; } From ab13030ae35a34d0e32e59f6dcd988ff2dcd21e1 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 17 Jul 2025 10:47:54 +0300 Subject: [PATCH 47/94] Styling fixes. --- .../modules/rule/engine/core/request/RequestSpecification.java | 3 ++- .../core/request/schema/functions/AdUnitCodeInFunction.java | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index af22172e26c..116cc550692 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -31,6 +31,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.TcfInScopeFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.UserFpdAvailableFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; +import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidResultFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidSchemaFunctionException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; @@ -97,7 +98,7 @@ public SchemaFunction schemaFunctionByName(String name) { public ResultFunction resultFunctionByName(String name) { final ResultFunction function = resultFunctions.get(name); if (function == null) { - throw new InvalidSchemaFunctionException(name); + throw new InvalidResultFunctionException(name); } return function; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 8fb99011b4a..3b4ab2faf9b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -29,7 +29,6 @@ public class AdUnitCodeInFunction implements SchemaFunction arguments) { final RequestSchemaContext context = arguments.getOperand(); final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); - ; final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() From 20ef8bd632f24801944710d448267d989dc67596 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 23 Jul 2025 13:38:19 +0300 Subject: [PATCH 48/94] Fixed misplaced modelVersion and analyticsKey fields. --- .../rule/engine/config/RuleEngineModuleConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java index f25cc1ff49a..74e256ca6a5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java @@ -43,7 +43,7 @@ StageConfigParser proces randomGenerator, Stage.processed_auction_request, new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), - (schema, ruleTree, modelVersion, analyticsKey) -> + (schema, ruleTree, analyticsKey, modelVersion) -> new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); } From 812ef28721071b68b1e67ada2dd73bbd874e5a4f Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 23 Jul 2025 13:47:00 +0300 Subject: [PATCH 49/94] Fixed minor remarks. --- .../result/functions/filter/AnalyticsMapper.java | 11 ++++++++--- .../request/result/functions/log/AnalyticsMapper.java | 9 ++++++--- .../request/schema/functions/DataCenterFunction.java | 2 +- .../schema/functions/DataCenterInFunction.java | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java index 6e6c3b844ac..0d21e8d04d9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -1,5 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.BidRejectionReason; @@ -78,8 +79,12 @@ public static Tags toTags(ObjectMapper mapper, } - private record AnalyticsData(String analyticsKey, String analyticsValue, String modelVersion, String ruleFired, - String resultFunction, List biddersRemoved, BidRejectionReason seatNonBid) { - + private record AnalyticsData(@JsonProperty("analyticsKey") String analyticsKey, + @JsonProperty("analyticsValue") String analyticsValue, + @JsonProperty("modelVersion") String modelVersion, + @JsonProperty("conditionFired") String conditionFired, + @JsonProperty("resultFunction") String resultFunction, + @JsonProperty("biddersRemoved") List biddersRemoved, + @JsonProperty("seatNonBid") BidRejectionReason seatNonBid) { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java index 9136dcd3ecc..5997c7a0c8c 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java @@ -1,5 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; @@ -52,8 +53,10 @@ public static Tags toTags(ObjectMapper mapper, } - private record AnalyticsData(String analyticsKey, String analyticsValue, String modelVersion, String ruleFired, - String resultFunction) { - + private record AnalyticsData(@JsonProperty("analyticsKey") String analyticsKey, + @JsonProperty("analyticsValue") String analyticsValue, + @JsonProperty("modelVersion") String modelVersion, + @JsonProperty("conditionFired") String conditionFired, + @JsonProperty("resultFunction") String resultFunction) { } } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java index 1c815844326..217535e516b 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java @@ -9,7 +9,7 @@ public class DataCenterFunction implements SchemaFunction { - public static final String NAME = "datacenter"; + public static final String NAME = "dataCenter"; @Override public String extract(SchemaFunctionArguments arguments) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java index 39edb5bce99..438c0bf1254 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java @@ -11,7 +11,7 @@ public class DataCenterInFunction implements SchemaFunction { - public static final String NAME = "datacenterIn"; + public static final String NAME = "dataCenterIn"; private static final String DATACENTERS_FIELD = "datacenters"; From 7afa1dd777b6f552a82fc699d637d066ee620e46 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 24 Jul 2025 17:08:36 +0300 Subject: [PATCH 50/94] Fixed minor remarks. --- .../rule/engine/core/request/RequestSpecification.java | 2 +- .../core/request/schema/functions/MediaTypeInFunction.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 116cc550692..2ecc180c5e4 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -46,7 +46,7 @@ public class RequestSpecification implements StageSpecification { public static final Set PER_IMP_SCHEMA_FUNCTIONS = - Set.of(AdUnitCodeFunction.NAME, MediaTypeInFunction.NAME); + Set.of(AdUnitCodeFunction.NAME, AdUnitCodeInFunction.NAME, MediaTypeInFunction.NAME); private final Map> schemaFunctions; private final Map> resultFunctions; diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index 84a2432cf40..e15c2b4b258 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -27,11 +27,11 @@ public class MediaTypeInFunction implements SchemaFunction public String extract(SchemaFunctionArguments arguments) { final RequestSchemaContext context = arguments.getOperand(); - final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId();; + final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() - .filter(imp -> StringUtils.equals(impId, impId)) + .filter(imp -> StringUtils.equals(imp.getId(), impId)) .findFirst() .orElseThrow(() -> new IllegalStateException( "Critical error in rules engine. Imp id of absent imp supplied")); From a105ab7b0087f08021b2f20b92f107d920e70ff5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 24 Jul 2025 21:19:04 +0300 Subject: [PATCH 51/94] More unit tests. --- .../test/core/rules/CompositeRuleTest.java | 55 --------- .../core/rules/DefaultActionRuleTest.java | 78 ------------- .../functions/AdUnitCodeFunctionTest.java | 104 ++++++++++++++++++ .../schema/functions/BundleFunctionTest.java | 56 ++++++++++ .../core/rules/AlternativeActionRuleTest.java | 22 ++-- .../engine/core/rules/CompositeRuleTest.java | 62 +++++++++++ .../core/rules/DefaultActionRuleTest.java | 78 +++++++++++++ .../engine}/core/rules/WeightedRuleTest.java | 16 ++- .../engine}/core/rules/tree/RuleTreeTest.java | 17 ++- 9 files changed, 326 insertions(+), 162 deletions(-) delete mode 100644 extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java delete mode 100644 extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java create mode 100644 extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java create mode 100644 extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java rename extra/modules/rule-engine/src/test/{ => java/org/prebid/server/hooks/modules/rule/engine}/core/rules/AlternativeActionRuleTest.java (72%) create mode 100644 extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java create mode 100644 extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java rename extra/modules/rule-engine/src/test/{ => java/org/prebid/server/hooks/modules/rule/engine}/core/rules/WeightedRuleTest.java (53%) rename extra/modules/rule-engine/src/test/{ => java/org/prebid/server/hooks/modules/rule/engine}/core/rules/tree/RuleTreeTest.java (65%) diff --git a/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java deleted file mode 100644 index ba3560bd2be..00000000000 --- a/extra/modules/rule-engine/src/test/core/rules/CompositeRuleTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package core.rules; - -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; -import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class CompositeRuleTest { - - private static final Object VALUE = new Object(); - - @Test - public void processShouldAccumulateResultFromAllSubrules() { - // given - final Rule firstRule = (Rule) mock(Rule.class); - given(firstRule.process(any())).willAnswer(invocationOnMock -> RuleResult.of( - UpdateResult.updated(invocationOnMock.getArgument(0)), - TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))))); - - final Rule secondRule = (Rule) mock(Rule.class); - given(secondRule.process(any())).willAnswer(invocationOnMock -> RuleResult.of( - UpdateResult.updated(invocationOnMock.getArgument(0)), - TagsImpl.of(singletonList(ActivityImpl.of("secondActivity", "success", emptyList()))))); - - final CompositeRule target = CompositeRule.of(asList(firstRule, secondRule)); - - // when - final RuleResult result = target.process(VALUE); - - // then - final Tags expectedTags = TagsImpl.of( - asList(ActivityImpl.of("firstActivity", "success", emptyList()), - ActivityImpl.of("secondActivity", "success", emptyList()))); - - assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); - - verify(firstRule).process(VALUE); - verify(secondRule).process(VALUE); - } -} diff --git a/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java b/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java deleted file mode 100644 index 5f489159f4c..00000000000 --- a/extra/modules/rule-engine/src/test/core/rules/DefaultActionRuleTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package core.rules; - -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; -import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; -import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class DefaultActionRuleTest { - - private static final Object VALUE = new Object(); - - @Test - public void processShouldAccumulateResultFromAllRuleActions() { - // given - final RuleAction firstAction = (RuleAction) mock(RuleAction.class); - final ResultFunction firstFunction = (ResultFunction) mock(ResultFunction.class); - - given(firstFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("firstAction")); - given(firstAction.getFunction()).willReturn(firstFunction); - given(firstAction.getConfig()).willReturn(emptyList()); - - - final RuleAction secondAction = (RuleAction) mock(RuleAction.class); - final ResultFunction secondFunction = (ResultFunction) mock(ResultFunction.class); - - given(secondFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("secondAction")); - given(secondAction.getFunction()).willReturn(secondFunction); - given(secondAction.getConfig()).willReturn(emptyList()); - - final DefaultActionRule target = new DefaultActionRule<>( - asList(firstAction, secondAction), "analyticsKey", "modelVersion"); - - // when - final RuleResult result = target.process(VALUE); - - // then - final Tags expectedTags = TagsImpl.of( - asList(ActivityImpl.of("firstAction", "success", emptyList()), - ActivityImpl.of("secondAction", "success", emptyList()))); - - assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); - - verify(firstFunction).apply( - ResultFunctionArguments.of( - VALUE, - emptyList(), - InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); - verify(secondFunction).apply( - ResultFunctionArguments.of( - VALUE, - emptyList(), - InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); - } - - private static Answer> prepareResultFunctionAnswer(String activityName) { - return invocation -> RuleResult.of( - UpdateResult.updated(((ResultFunctionArguments) invocation.getArgument(0)).getOperand()), - TagsImpl.of(singletonList(ActivityImpl.of(activityName, "success", emptyList())))); - } -} diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java new file mode 100644 index 00000000000..0d9ea9ce10c --- /dev/null +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java @@ -0,0 +1,104 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class AdUnitCodeFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final AdUnitCodeFunction target = new AdUnitCodeFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnGpidWhenPresent() { + // given + final Imp imp = Imp.builder() + .id("impId") + .ext(mapper.createObjectNode().put("gpid", "gpid")) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("gpid"); + } + + @Test + public void extractShouldReturnTagidWhenGpidAbsentAndTagidPresent() { + // given + final Imp imp = Imp.builder() + .id("impId") + .tagid("tagId") + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("tagId"); + } + + @Test + public void extractShouldReturnPbAdSlotWhenGpidAndTagidAreAbsent() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + + final Imp imp = Imp.builder().id("impId").ext(ext).build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("pbadslot"); + } + + @Test + public void extractShouldReturnStoredRequestIdWhenGpidAndTagidAndPbAdSlotAreAbsent() { + // given + final ObjectNode prebid = mapper.createObjectNode(); + prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); + final ObjectNode ext = mapper.createObjectNode(); + ext.set("prebid", prebid); + + final Imp imp = Imp.builder().id("impId").ext(ext).build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("srid"); + } +} diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java new file mode 100644 index 00000000000..2b7b766ec34 --- /dev/null +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java @@ -0,0 +1,56 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class BundleFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final BundleFunction target = new BundleFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldExtractBundle() { + // given + final BidRequest bidRequest = BidRequest.builder() + .app(App.builder().bundle("bundle").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("bundle"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenBundleIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java similarity index 72% rename from extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java rename to extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java index 6bd2ed9fce1..8a46292709b 100644 --- a/extra/modules/rule-engine/src/test/core/rules/AlternativeActionRuleTest.java +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java @@ -1,4 +1,4 @@ -package core.rules; +package org.prebid.server.hooks.modules.rule.engine.core.rules; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -23,26 +23,26 @@ public class AlternativeActionRuleTest { private static final RuleResult ALTERNATIVE_RESULT = RuleResult.unaltered(new Object()); @Mock(strictness = LENIENT) - private Rule delegate; + private Rule delegate; @Mock(strictness = LENIENT) - private Rule alternative; + private Rule alternative; - private AlternativeActionRule target; + private AlternativeActionRule target; @BeforeEach public void setUp() { - target = new AlternativeActionRule<>(delegate, alternative); + target = AlternativeActionRule.of(delegate, alternative); } @Test public void processShouldReturnDelegateResult() { // given - given(delegate.process(any())).willReturn(DELEGATE_RESULT); - given(alternative.process(any())).willReturn(ALTERNATIVE_RESULT); + given(delegate.process(any(), any())).willReturn(DELEGATE_RESULT); + given(alternative.process(any(), any())).willReturn(ALTERNATIVE_RESULT); // when - final RuleResult result = target.process(new Object()); + final RuleResult result = target.process(new Object(), new Object()); // then assertThat(result).isEqualTo(DELEGATE_RESULT); @@ -52,11 +52,11 @@ public void processShouldReturnDelegateResult() { @Test public void processShouldReturnAlternativeResultWhenNoMatchingRuleException() { // given - given(delegate.process(any())).willThrow(new NoMatchingRuleException()); - given(alternative.process(any())).willReturn(ALTERNATIVE_RESULT); + given(delegate.process(any(), any())).willThrow(new NoMatchingRuleException()); + given(alternative.process(any(), any())).willReturn(ALTERNATIVE_RESULT); // when - final RuleResult result = target.process(new Object()); + final RuleResult result = target.process(new Object(), new Object()); // then assertThat(result).isEqualTo(ALTERNATIVE_RESULT); diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java new file mode 100644 index 00000000000..c350a5b56a6 --- /dev/null +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java @@ -0,0 +1,62 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; + +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class CompositeRuleTest { + + private static final Object VALUE = new Object(); + + @Test + public void processShouldAccumulateResultFromAllSubrules() { + // given + final Rule firstRule = (Rule) mock(Rule.class); + given(firstRule.process(any(), any())).willAnswer(invocationOnMock -> RuleResult.of( + UpdateResult.updated(invocationOnMock.getArgument(0)), + TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))), + singletonList(SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))))); + + final Rule secondRule = (Rule) mock(Rule.class); + given(secondRule.process(any(), any())).willAnswer(invocationOnMock -> RuleResult.of( + UpdateResult.updated(invocationOnMock.getArgument(0)), + TagsImpl.of(singletonList(ActivityImpl.of("secondActivity", "success", emptyList()))), + singletonList(SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))))); + + final CompositeRule target = CompositeRule.of(asList(firstRule, secondRule)); + + // when + final RuleResult result = target.process(VALUE, new Object()); + + // then + final Tags expectedTags = TagsImpl.of( + asList(ActivityImpl.of("firstActivity", "success", emptyList()), + ActivityImpl.of("secondActivity", "success", emptyList()))); + + List expectedNonBids = List.of( + SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID))), + SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); + + assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags, expectedNonBids)); + + verify(firstRule).process(eq(VALUE), any()); + verify(secondRule).process(eq(VALUE), any()); + } +} diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java new file mode 100644 index 00000000000..b72bbac0899 --- /dev/null +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java @@ -0,0 +1,78 @@ +//package org.prebid.server.hooks.modules.rule.engine.core.rules; +// +//import org.junit.jupiter.api.Test; +//import org.mockito.stubbing.Answer; +//import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +//import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +//import org.prebid.server.hooks.v1.analytics.Tags; +//import org.prebid.server.model.UpdateResult; +// +//import static java.util.Arrays.asList; +//import static java.util.Collections.emptyList; +//import static java.util.Collections.emptyMap; +//import static java.util.Collections.singletonList; +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.BDDMockito.given; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.verify; +// +//public class DefaultActionRuleTest { +// +// private static final Object VALUE = new Object(); +// +// @Test +// public void processShouldAccumulateResultFromAllRuleActions() { +// // given +// final RuleAction firstAction = (RuleAction) mock(RuleAction.class); +// final ResultFunction firstFunction = (ResultFunction) mock(ResultFunction.class); +// +// given(firstFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("firstAction")); +// given(firstAction.getFunction()).willReturn(firstFunction); +// given(firstAction.getConfig()).willReturn(emptyList()); +// +// +// final RuleAction secondAction = (RuleAction) mock(RuleAction.class); +// final ResultFunction secondFunction = (ResultFunction) mock(ResultFunction.class); +// +// given(secondFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("secondAction")); +// given(secondAction.getFunction()).willReturn(secondFunction); +// given(secondAction.getConfig()).willReturn(emptyList()); +// +// final DefaultActionRule target = new DefaultActionRule<>( +// asList(firstAction, secondAction), "analyticsKey", "modelVersion"); +// +// // when +// final RuleResult result = target.process(VALUE); +// +// // then +// final Tags expectedTags = TagsImpl.of( +// asList(ActivityImpl.of("firstAction", "success", emptyList()), +// ActivityImpl.of("secondAction", "success", emptyList()))); +// +// assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); +// +// verify(firstFunction).apply( +// ResultFunctionArguments.of( +// VALUE, +// emptyList(), +// InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); +// verify(secondFunction).apply( +// ResultFunctionArguments.of( +// VALUE, +// emptyList(), +// InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); +// } +// +// private static Answer> prepareResultFunctionAnswer(String activityName) { +// return invocation -> RuleResult.of( +// UpdateResult.updated(((ResultFunctionArguments) invocation.getArgument(0)).getOperand()), +// TagsImpl.of(singletonList(ActivityImpl.of(activityName, "success", emptyList())))); +// } +//} diff --git a/extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java similarity index 53% rename from extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java rename to extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java index 645bf335979..db649f33dab 100644 --- a/extra/modules/rule-engine/src/test/core/rules/WeightedRuleTest.java +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java @@ -1,15 +1,12 @@ -package core.rules; +package org.prebid.server.hooks.modules.rule.engine.core.rules; import org.junit.jupiter.api.Test; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -import org.prebid.server.hooks.modules.rule.engine.core.rules.WeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; import java.util.random.RandomGenerator; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -18,17 +15,18 @@ public class WeightedRuleTest { @Test public void processShouldUtilizeRuleFromWeightedList() { // given - final WeightedList> ruleList = (WeightedList>) mock(WeightedList.class); + final WeightedList> ruleList = + (WeightedList>) mock(WeightedList.class); final RuleResult stub = RuleResult.unaltered(new Object()); - given(ruleList.getForSeed(anyDouble())).willReturn(value -> stub); + given(ruleList.getForSeed(anyInt())).willReturn((left, right) -> stub); final RandomGenerator randomGenerator = mock(RandomGenerator.class); given(randomGenerator.nextDouble()).willReturn(0.5); - final WeightedRule rule = new WeightedRule<>(randomGenerator, ruleList); + final RandomWeightedRule rule = new RandomWeightedRule<>(randomGenerator, ruleList); // when - final RuleResult result = rule.process(rule); + final RuleResult result = rule.process(new Object(), new Object()); // then assertThat(result).isEqualTo(stub); diff --git a/extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java similarity index 65% rename from extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java rename to extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java index e79e6042aff..9275612dd2b 100644 --- a/extra/modules/rule-engine/src/test/core/rules/tree/RuleTreeTest.java +++ b/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java @@ -1,10 +1,9 @@ -package core.rules.tree; +package org.prebid.server.hooks.modules.rule.engine.core.rules.tree; import org.junit.jupiter.api.Test; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; -import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleNode; -import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import java.util.List; import java.util.Map; import static java.util.Arrays.asList; @@ -28,11 +27,11 @@ public void getValueShouldReturnExpectedValue() { final RuleTree tree = new RuleTree<>(new RuleNode.IntermediateNode<>(subnodes), 2); // when and then - assertThat(tree.getValue(asList("A", "B"))).isEqualTo("AB"); - assertThat(tree.getValue(asList("A", "C"))).isEqualTo("AC"); - assertThat(tree.getValue(asList("B", "B"))).isEqualTo("BB"); - assertThat(tree.getValue(asList("B", "C"))).isEqualTo("BC"); - assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.getValue(asList("C", "B"))); - assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.getValue(singletonList("C"))); + assertThat(tree.lookup(asList("A", "B"))).isEqualTo(LookupResult.of("AB", List.of("A", "B"))); + assertThat(tree.lookup(asList("A", "C"))).isEqualTo(LookupResult.of("AC", List.of("A", "*"))); + assertThat(tree.lookup(asList("B", "B"))).isEqualTo(LookupResult.of("BB", List.of("B", "B"))); + assertThat(tree.lookup(asList("B", "C"))).isEqualTo(LookupResult.of("BC", List.of("B", "C"))); + assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.lookup(asList("C", "B"))); + assertThatExceptionOfType(NoMatchingRuleException.class).isThrownBy(() -> tree.lookup(singletonList("C"))); } } From 9d5a7c95c37c82d074e1b6f4e5947fbf7288f005 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Thu, 24 Jul 2025 21:36:47 +0300 Subject: [PATCH 52/94] Renamed rule-engine to pb-rule-engine, bumped version to latest pbs version. --- extra/bundle/pom.xml | 2 +- .../{rule-engine => pb-rule-engine}/pom.xml | 6 ++--- .../src/lombok.config | 0 .../PbRuleEngineModuleConfiguration.java} | 22 +++++++++---------- .../core/config/AccountConfigParser.java | 0 .../rule/engine/core/config/RuleParser.java | 0 .../engine/core/config/StageConfigParser.java | 0 .../core/config/model/AccountConfig.java | 0 .../core/config/model/AccountRuleConfig.java | 0 .../core/config/model/ModelGroupConfig.java | 0 .../config/model/ResultFunctionConfig.java | 0 .../core/config/model/RuleSetConfig.java | 0 .../config/model/SchemaFunctionConfig.java | 0 .../rule/engine/core/request/Granularity.java | 0 .../core/request/RequestMatchingRule.java | 0 .../core/request/RequestSpecification.java | 0 .../request/context/RequestResultContext.java | 0 .../request/context/RequestSchemaContext.java | 0 .../functions/filter/AnalyticsMapper.java | 0 .../filter/ExcludeBiddersFunction.java | 0 .../filter/FilterBiddersFunction.java | 0 .../filter/FilterBiddersFunctionConfig.java | 0 .../functions/filter/FilterBiddersResult.java | 0 .../result/functions/filter/FilterUtils.java | 0 .../filter/IncludeBiddersFunction.java | 0 .../result/functions/log/AnalyticsMapper.java | 0 .../result/functions/log/LogATagFunction.java | 0 .../schema/functions/AdUnitCodeFunction.java | 0 .../functions/AdUnitCodeInFunction.java | 0 .../schema/functions/BundleFunction.java | 0 .../schema/functions/BundleInFunction.java | 0 .../schema/functions/ChannelFunction.java | 0 .../schema/functions/DataCenterFunction.java | 0 .../functions/DataCenterInFunction.java | 0 .../functions/DeviceCountryFunction.java | 0 .../functions/DeviceCountryInFunction.java | 0 .../schema/functions/DeviceTypeFunction.java | 0 .../functions/DeviceTypeInFunction.java | 0 .../schema/functions/DomainFunction.java | 0 .../schema/functions/DomainInFunction.java | 0 .../functions/EidAvailableFunction.java | 0 .../schema/functions/EidInFunction.java | 0 .../functions/FpdAvailableFunction.java | 0 .../functions/GppSidAvailableFunction.java | 0 .../schema/functions/GppSidInFunction.java | 0 .../schema/functions/MediaTypeInFunction.java | 0 .../schema/functions/PrebidKeyFunction.java | 0 .../schema/functions/TcfInScopeFunction.java | 0 .../functions/UserFpdAvailableFunction.java | 0 .../functions/util/AdUnitCodeUtils.java | 0 .../schema/functions/util/DomainUtils.java | 0 .../core/rules/AlternativeActionRule.java | 0 .../rule/engine/core/rules/CompositeRule.java | 0 .../engine/core/rules/DefaultActionRule.java | 0 .../core/rules/MatchingRuleFactory.java | 0 .../rule/engine/core/rules/NoOpRule.java | 0 .../rule/engine/core/rules/PerStageRule.java | 0 .../engine/core/rules/RandomWeightedRule.java | 0 .../modules/rule/engine/core/rules/Rule.java | 0 .../rule/engine/core/rules/RuleConfig.java | 0 .../rule/engine/core/rules/RuleResult.java | 0 .../engine/core/rules/StageSpecification.java | 0 .../InvalidMatcherConfiguration.java | 0 .../InvalidResultFunctionException.java | 0 .../InvalidSchemaFunctionException.java | 0 .../exception/NoMatchingRuleException.java | 0 .../rules/result/InfrastructureArguments.java | 0 .../core/rules/result/ResultFunction.java | 0 .../rules/result/ResultFunctionArguments.java | 0 .../engine/core/rules/result/RuleAction.java | 0 .../rule/engine/core/rules/schema/Schema.java | 0 .../core/rules/schema/SchemaFunction.java | 0 .../rules/schema/SchemaFunctionArguments.java | 0 .../rules/schema/SchemaFunctionHolder.java | 0 .../schema/functions/PercentFunction.java | 0 .../engine/core/rules/tree/LookupResult.java | 0 .../rule/engine/core/rules/tree/RuleNode.java | 0 .../rule/engine/core/rules/tree/RuleTree.java | 0 .../core/rules/tree/RuleTreeFactory.java | 0 .../ConfigurationValidationException.java | 0 .../core/util/NoMatchingValueException.java | 0 .../engine/core/util/ValidationUtils.java | 0 .../rule/engine/core/util/WeightedEntry.java | 0 .../rule/engine/core/util/WeightedList.java | 0 .../rule/engine/v1/PbRuleEngineModule.java} | 8 +++---- ...uleEngineProcessedAuctionRequestHook.java} | 10 ++++----- .../functions/AdUnitCodeFunctionTest.java | 0 .../schema/functions/BundleFunctionTest.java | 0 .../core/rules/AlternativeActionRuleTest.java | 0 .../engine/core/rules/CompositeRuleTest.java | 0 .../core/rules/DefaultActionRuleTest.java | 0 .../engine/core/rules/WeightedRuleTest.java | 0 .../engine/core/rules/tree/RuleTreeTest.java | 0 extra/modules/pom.xml | 2 +- 94 files changed, 25 insertions(+), 25 deletions(-) rename extra/modules/{rule-engine => pb-rule-engine}/pom.xml (80%) rename extra/modules/{rule-engine => pb-rule-engine}/src/lombok.config (100%) rename extra/modules/{rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java => pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java} (75%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java (100%) rename extra/modules/{rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java => pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java} (73%) rename extra/modules/{rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java => pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java} (89%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java (100%) rename extra/modules/{rule-engine => pb-rule-engine}/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java (100%) diff --git a/extra/bundle/pom.xml b/extra/bundle/pom.xml index 793ab78220c..7f5077057f1 100644 --- a/extra/bundle/pom.xml +++ b/extra/bundle/pom.xml @@ -57,7 +57,7 @@ org.prebid.server.hooks.modules - rule-engine + pb-rule-engine ${project.version} diff --git a/extra/modules/rule-engine/pom.xml b/extra/modules/pb-rule-engine/pom.xml similarity index 80% rename from extra/modules/rule-engine/pom.xml rename to extra/modules/pb-rule-engine/pom.xml index 9c8dc003c6e..fae5adb6135 100644 --- a/extra/modules/rule-engine/pom.xml +++ b/extra/modules/pb-rule-engine/pom.xml @@ -5,11 +5,11 @@ org.prebid.server.hooks.modules all-modules - 3.25.0-SNAPSHOT + 3.29.0-SNAPSHOT - rule-engine + pb-rule-engine - rule-engine + pb-rule-engine Rule engine module diff --git a/extra/modules/rule-engine/src/lombok.config b/extra/modules/pb-rule-engine/src/lombok.config similarity index 100% rename from extra/modules/rule-engine/src/lombok.config rename to extra/modules/pb-rule-engine/src/lombok.config diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java similarity index 75% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java index 74e256ca6a5..61436f2a0cc 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/RuleEngineModuleConfiguration.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java @@ -12,7 +12,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.v1.RuleEngineModule; +import org.prebid.server.hooks.modules.rule.engine.v1.PbRuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -24,12 +24,12 @@ import java.util.random.RandomGenerator; @Configuration -@ConditionalOnProperty(prefix = "hooks." + RuleEngineModule.CODE, name = "enabled", havingValue = "true") -public class RuleEngineModuleConfiguration { +@ConditionalOnProperty(prefix = "hooks." + PbRuleEngineModule.CODE, name = "enabled", havingValue = "true") +public class PbRuleEngineModuleConfiguration { @Bean - RuleEngineModule ruleEngineModule(RuleParser ruleParser) { - return new RuleEngineModule(ruleParser); + PbRuleEngineModule ruleEngineModule(RuleParser ruleParser) { + return new PbRuleEngineModule(ruleParser); } @Bean @@ -57,12 +57,12 @@ AccountConfigParser accountConfigParser( @Bean RuleParser ruleParser( - @Value("${hooks.rule-engine.rule-cache.expire-after-minutes}") long cacheExpireAfterMinutes, - @Value("${hooks.rule-engine.rule-cache.max-size}") long cacheMaxSize, - @Value("${hooks.rule-engine.rule-parsing.retry-initial-delay-millis}") long delay, - @Value("${hooks.rule-engine.rule-parsing.retry-max-delay-millis}") long maxDelay, - @Value("${hooks.rule-engine.rule-parsing.retry-exponential-factor}") double factor, - @Value("${hooks.rule-engine.rule-parsing.retry-exponential-jitter}") double jitter, + @Value("${hooks.pb-rule-engine.rule-cache.expire-after-minutes}") long cacheExpireAfterMinutes, + @Value("${hooks.pb-rule-engine.rule-cache.max-size}") long cacheMaxSize, + @Value("${hooks.pb-rule-engine.rule-parsing.retry-initial-delay-millis}") long delay, + @Value("${hooks.pb-rule-engine.rule-parsing.retry-max-delay-millis}") long maxDelay, + @Value("${hooks.pb-rule-engine.rule-parsing.retry-exponential-factor}") double factor, + @Value("${hooks.pb-rule-engine.rule-parsing.retry-exponential-jitter}") double jitter, AccountConfigParser accountConfigParser, Vertx vertx, Clock clock) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountRuleConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/SchemaFunctionConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/AdUnitCodeUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/Rule.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidMatcherConfiguration.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidResultFunctionException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/InvalidSchemaFunctionException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/exception/NoMatchingRuleException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/LookupResult.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleNode.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ConfigurationValidationException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/NoMatchingValueException.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java similarity index 100% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java similarity index 73% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java index ad861eaba63..01543b7f5ed 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineModule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java @@ -8,15 +8,15 @@ import java.util.Collection; import java.util.Collections; -public class RuleEngineModule implements Module { +public class PbRuleEngineModule implements Module { - public static final String CODE = "rule-engine"; + public static final String CODE = "pb-rule-engine"; private final Collection> hooks; - public RuleEngineModule(RuleParser ruleParser) { + public PbRuleEngineModule(RuleParser ruleParser) { this.hooks = Collections.singleton( - new RuleEngineProcessedAuctionRequestHook(ruleParser)); + new PbRuleEngineProcessedAuctionRequestHook(ruleParser)); } @Override diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java similarity index 89% rename from extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index 0e0271ee041..bc70c234403 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/RuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -22,13 +22,13 @@ import java.util.Objects; -public class RuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { +public class PbRuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { - private static final String CODE = "rule-engine-processed-auction-request"; + private static final String CODE = "pb-rule-engine-processed-auction-request"; private final RuleParser ruleParser; - public RuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { + public PbRuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { this.ruleParser = Objects.requireNonNull(ruleParser); } @@ -49,8 +49,8 @@ public Future> call(AuctionRequestPayloa .map(PerStageRule::processedAuctionRequestRule) .map(rule -> rule.process( bidRequest, RequestResultContext.of(context, Granularity.Request.instance()))) - .flatMap(RuleEngineProcessedAuctionRequestHook::succeeded) - .recover(RuleEngineProcessedAuctionRequestHook::failure); + .flatMap(PbRuleEngineProcessedAuctionRequestHook::succeeded) + .recover(PbRuleEngineProcessedAuctionRequestHook::failure); } private static Future> succeeded(RuleResult result) { diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java diff --git a/extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java similarity index 100% rename from extra/modules/rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeTest.java diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml index be9ae825f8a..5efb3c7d2ee 100644 --- a/extra/modules/pom.xml +++ b/extra/modules/pom.xml @@ -24,7 +24,7 @@ pb-response-correction greenbids-real-time-data pb-request-correction - rule-engine + pb-rule-engine From 56901d7fd5bd3e2d472e2b17890997b00c0855a1 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 5 Aug 2025 15:43:45 +0300 Subject: [PATCH 53/94] Added unit tests for some schema functions. --- .../functions/AdUnitCodeFunctionTest.java | 13 ++ .../functions/AdUnitCodeInFunctionTest.java | 188 ++++++++++++++++++ .../schema/functions/BundleFunctionTest.java | 2 +- .../functions/BundleInFunctionTest.java | 103 ++++++++++ 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java index 0d9ea9ce10c..e528a4a213c 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java @@ -101,4 +101,17 @@ public void extractShouldReturnStoredRequestIdWhenGpidAndTagidAndPbAdSlotAreAbse // when and then assertThat(target.extract(arguments)).isEqualTo("srid"); } + + @Test + public void extractShouldFallbackToUndefinedWhenAllAdUnitCodeSourcesAreAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java new file mode 100644 index 00000000000..8e7a15b8c8d --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java @@ -0,0 +1,188 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.NumericNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class AdUnitCodeInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final AdUnitCodeInFunction target = new AdUnitCodeInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'codes' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenCodesFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'codes' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenCodesFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("codes", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'codes' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenCodesFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode codesNode = mapper.createArrayNode(); + codesNode.add(TextNode.valueOf("test")); + codesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("codes", codesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'codes' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenGpidPresentInConfiguredCodes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .ext(mapper.createObjectNode().put("gpid", "gpid")) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + givenConfigWithCodes("gpid")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenTagidPresentAndGpidIsAbsentInConfiguredCodes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .tagid("tagId") + .ext(mapper.createObjectNode().put("gpid", "gpid")) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + givenConfigWithCodes("tagId")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenPbAdSlotPresentAndGpidAndTagidAreAbsentInConfiguredCodes() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + ext.set("gpid", TextNode.valueOf("gpid")); + + final Imp imp = Imp.builder() + .id("impId") + .tagid("tagId") + .ext(ext) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + givenConfigWithCodes("pbadslot")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenSridPresentAndGpidAndTagidAndPbAdSlotAreAbsentInConfiguredCodes() { + // given + final ObjectNode prebid = mapper.createObjectNode(); + prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); + final ObjectNode ext = mapper.createObjectNode(); + ext.set("prebid", prebid); + ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + ext.set("gpid", TextNode.valueOf("gpid")); + + final Imp imp = Imp.builder() + .id("impId") + .tagid("tagId") + .ext(ext) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + givenConfigWithCodes("srid")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenAdUnitCodesDoesNotMatchConfiguredCodes() { + // given + final ObjectNode prebid = mapper.createObjectNode(); + prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); + final ObjectNode ext = mapper.createObjectNode(); + ext.set("prebid", prebid); + ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + ext.set("gpid", TextNode.valueOf("gpid")); + + final Imp imp = Imp.builder() + .id("impId") + .tagid("tagId") + .ext(ext) + .build(); + + final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), + givenConfigWithCodes("adUnitCode")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + + private ObjectNode givenConfigWithCodes(String... codes) { + final ArrayNode codesNode = mapper.createArrayNode(); + Arrays.stream(codes).map(TextNode::valueOf).forEach(codesNode::add); + return mapper.createObjectNode().set("codes", codesNode); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java index 2b7b766ec34..46e7899c111 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java @@ -27,7 +27,7 @@ public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { } @Test - public void extractShouldExtractBundle() { + public void extractShouldReturnBundle() { // given final BidRequest bidRequest = BidRequest.builder() .app(App.builder().bundle("bundle").build()) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java new file mode 100644 index 00000000000..db5d703ca0f --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java @@ -0,0 +1,103 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class BundleInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final BundleInFunction target = new BundleInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'bundles' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenCodesFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'bundles' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("bundles", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'bundles' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode bundlesNode = mapper.createArrayNode(); + bundlesNode.add(TextNode.valueOf("test")); + bundlesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("bundles", bundlesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'bundles' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenBundlePresentInConfiguredBundles() { + // given + final BidRequest bidRequest = BidRequest.builder() + .app(App.builder().bundle("bundle").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithBundles("bundle")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenBundleIsAbsentInConfiguredBundles() { + // given + final BidRequest bidRequest = BidRequest.builder() + .app(App.builder().bundle("bundle").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithBundles("expectedBundle")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private ObjectNode givenConfigWithBundles(String... bundles) { + final ArrayNode bundlesNode = mapper.createArrayNode(); + Arrays.stream(bundles).map(TextNode::valueOf).forEach(bundlesNode::add); + return mapper.createObjectNode().set("bundles", bundlesNode); + } +} From 673de00b070c1448fa85c2bb99b199e5e0684d1d Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 5 Aug 2025 16:02:51 +0300 Subject: [PATCH 54/94] Added unit tests for more schema functions. --- .../functions/AdUnitCodeFunctionTest.java | 6 +- .../functions/AdUnitCodeInFunctionTest.java | 1 - .../schema/functions/BundleFunctionTest.java | 7 +- .../functions/BundleInFunctionTest.java | 2 +- .../schema/functions/ChannelFunctionTest.java | 84 +++++++++++++++++++ .../functions/DataCenterFunctionTest.java | 58 +++++++++++++ .../functions/DataCenterInFunctionTest.java | 52 ++++++++++++ 7 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java index e528a4a213c..5dd25198789 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.junit.jupiter.api.Test; @@ -22,8 +23,11 @@ public class AdUnitCodeFunctionTest { @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(config)) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("No arguments allowed"); } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java index 8e7a15b8c8d..09e8bd63082 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.IntNode; -import com.fasterxml.jackson.databind.node.NumericNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java index 46e7899c111..72686483cf1 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java @@ -1,6 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; @@ -20,8 +22,11 @@ public class BundleFunctionTest { @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(config)) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("No arguments allowed"); } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java index db5d703ca0f..ceda9b2e550 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java @@ -33,7 +33,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { } @Test - public void validateConfigShouldThrowErrorWhenCodesFieldIsAbsent() { + public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { // when and then assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java new file mode 100644 index 00000000000..70f3236e7b7 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java @@ -0,0 +1,84 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ChannelFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final ChannelFunction target = new ChannelFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnChannel() { + // given + final ExtRequest ext = ExtRequest.of( + ExtRequestPrebid.builder() + .channel(ExtRequestPrebidChannel.of("channel")) + .build()); + + final BidRequest bidRequest = BidRequest.builder().ext(ext).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("channel"); + } + + @Test + public void extractShouldReturnWebWhenChannelIsPbjs() { + // given + final ExtRequest ext = ExtRequest.of( + ExtRequestPrebid.builder() + .channel(ExtRequestPrebidChannel.of("pbjs")) + .build()); + + final BidRequest bidRequest = BidRequest.builder().ext(ext).build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("web"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java new file mode 100644 index 00000000000..5f835c0effc --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java @@ -0,0 +1,58 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DataCenterFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DataCenterFunction target = new DataCenterFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnDataCenter() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("datacenter"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenDataCenterIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), null), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java new file mode 100644 index 00000000000..d879cf492ed --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java @@ -0,0 +1,52 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DataCenterInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DataCenterInFunction target = new DataCenterInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'datacenters' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'datacenters' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("datacenters", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'datacenters' is required and has to be an array of strings"); + } + + private ObjectNode givenConfigWithDataCenters(String... dataCenters) { + final ArrayNode dataCentersNode = mapper.createArrayNode(); + Arrays.stream(dataCenters).map(TextNode::valueOf).forEach(dataCentersNode::add); + return mapper.createObjectNode().set("bundles", dataCentersNode); + } +} From b38c7974763881a1909cdd3b63f6a433a033e50e Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 5 Aug 2025 16:20:48 +0300 Subject: [PATCH 55/94] Added unit tests for more schema functions. --- .../functions/DataCenterInFunctionTest.java | 49 ++++++++- .../functions/DeviceCountryFunctionTest.java | 65 +++++++++++ .../DeviceCountryInFunctionTest.java | 102 ++++++++++++++++++ .../functions/DeviceTypeFunctionTest.java | 61 +++++++++++ .../functions/DeviceTypeInFunctionTest.java | 101 +++++++++++++++++ 5 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java index d879cf492ed..0599b3f7326 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java @@ -2,13 +2,19 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class DataCenterInFunctionTest { @@ -44,9 +50,50 @@ public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { .hasMessage("Field 'datacenters' is required and has to be an array of strings"); } + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode datacentersNode = mapper.createArrayNode(); + datacentersNode.add(TextNode.valueOf("test")); + datacentersNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("datacenters", datacentersNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'datacenters' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenDataCenterPresentInConfiguredDatacenters() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDataCenters("datacenter")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenBundleIsAbsentInConfiguredBundles() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDataCenters("expectedDatacenter")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private ObjectNode givenConfigWithDataCenters(String... dataCenters) { final ArrayNode dataCentersNode = mapper.createArrayNode(); Arrays.stream(dataCenters).map(TextNode::valueOf).forEach(dataCentersNode::add); - return mapper.createObjectNode().set("bundles", dataCentersNode); + return mapper.createObjectNode().set("datacenters", dataCentersNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java new file mode 100644 index 00000000000..631aa2e9fdb --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java @@ -0,0 +1,65 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DeviceCountryFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DeviceCountryFunction target = new DeviceCountryFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnDeviceCountry() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder().geo(Geo.builder().country("country").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("country"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java new file mode 100644 index 00000000000..f8d1df9f3c4 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java @@ -0,0 +1,102 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Geo; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DeviceCountryInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DeviceCountryInFunction target = new DeviceCountryInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'countries' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'countries' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("countries", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'countries' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode countriesNode = mapper.createArrayNode(); + countriesNode.add(TextNode.valueOf("test")); + countriesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("countries", countriesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'countries' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenDeviceCountryPresentInConfiguredCountries() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder().geo(Geo.builder().country("country").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithCountries("country")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenDeviceCountryIsAbsentInConfiguredCountries() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithCountries("expectedCountry")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private ObjectNode givenConfigWithCountries(String... countries) { + final ArrayNode countriesNode = mapper.createArrayNode(); + Arrays.stream(countries).map(TextNode::valueOf).forEach(countriesNode::add); + return mapper.createObjectNode().set("countries", countriesNode); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java new file mode 100644 index 00000000000..fd8a0a75a0f --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java @@ -0,0 +1,61 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DeviceTypeFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DeviceTypeFunction target = new DeviceTypeFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnDeviceType() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder().devicetype(12345).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("12345"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java new file mode 100644 index 00000000000..e229f50b116 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java @@ -0,0 +1,101 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DeviceTypeInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DeviceTypeInFunction target = new DeviceTypeInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("types", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArrayOfIntegers() { + // given + final ArrayNode typesNode = mapper.createArrayNode(); + typesNode.add(TextNode.valueOf("test")); + typesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("types", typesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of integers"); + } + + @Test + public void extractShouldReturnTrueWhenDeviceTypePresentInConfiguredTypes() { + // given + final BidRequest bidRequest = BidRequest.builder() + .device(Device.builder().devicetype(12345).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithTypes(12345)); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenDeviceTypeIsAbsentInConfiguredTypes() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithTypes(1)); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private ObjectNode givenConfigWithTypes(int... types) { + final ArrayNode typesNode = mapper.createArrayNode(); + Arrays.stream(types).mapToObj(IntNode::valueOf).forEach(typesNode::add); + return mapper.createObjectNode().set("types", typesNode); + } +} From 4ca81827fc8a509e4586f818eef84415e1994ff2 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 6 Aug 2025 16:22:47 +0300 Subject: [PATCH 56/94] Added unit tests for more schema functions. --- .../schema/functions/DomainInFunction.java | 22 +- .../schema/functions/util/DomainUtils.java | 42 +++- .../schema/functions/DomainFunctionTest.java | 139 ++++++++++ .../functions/DomainInFunctionTest.java | 237 ++++++++++++++++++ 4 files changed, 431 insertions(+), 9 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java index dd51db9e673..7af83a62175 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.DomainUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; @@ -9,6 +10,11 @@ import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.util.StreamUtil; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + public class DomainInFunction implements SchemaFunction { public static final String NAME = "domainIn"; @@ -17,12 +23,22 @@ public class DomainInFunction implements SchemaFunction { @Override public String extract(SchemaFunctionArguments arguments) { - final String domain = DomainUtils.extractDomain(arguments.getOperand().getBidRequest()) - .orElse(UNDEFINED_RESULT); + final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + + final Set suppliedDomains = Stream.of( + DomainUtils.extractSitePublisherDomain(bidRequest), + DomainUtils.extractAppPublisherDomain(bidRequest), + DomainUtils.extractDoohPublisherDomain(bidRequest), + DomainUtils.extractSiteDomain(bidRequest), + DomainUtils.extractAppDomain(bidRequest), + DomainUtils.extractDoohDomain(bidRequest)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); final boolean matches = StreamUtil.asStream(arguments.getConfig().get(DOMAINS_FIELD).elements()) .map(JsonNode::asText) - .anyMatch(domain::equals); + .anyMatch(suppliedDomains::contains); return Boolean.toString(matches); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java index 9da8c7b1032..b9d57ef8aed 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/util/DomainUtils.java @@ -19,14 +19,44 @@ public static Optional extractDomain(BidRequest bidRequest) { } private static Optional extractPublisherDomain(BidRequest bidRequest) { - return Optional.ofNullable(bidRequest.getSite()).map(Site::getPublisher).map(Publisher::getDomain) - .or(() -> Optional.ofNullable(bidRequest.getApp()).map(App::getPublisher).map(Publisher::getDomain)) - .or(() -> Optional.ofNullable(bidRequest.getDooh()).map(Dooh::getPublisher).map(Publisher::getDomain)); + return extractSitePublisherDomain(bidRequest) + .or(() -> extractAppPublisherDomain(bidRequest)) + .or(() -> extractDoohPublisherDomain(bidRequest)); + } + + public static Optional extractSitePublisherDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()) + .map(Site::getPublisher) + .map(Publisher::getDomain); + } + + public static Optional extractAppPublisherDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getApp()) + .map(App::getPublisher) + .map(Publisher::getDomain); + } + + public static Optional extractDoohPublisherDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getDooh()) + .map(Dooh::getPublisher) + .map(Publisher::getDomain); } private static Optional extractPlainDomain(BidRequest bidRequest) { - return Optional.ofNullable(bidRequest.getSite()).map(Site::getDomain) - .or(() -> Optional.ofNullable(bidRequest.getApp()).map(App::getDomain)) - .or(() -> Optional.ofNullable(bidRequest.getDooh()).map(Dooh::getDomain)); + return extractSiteDomain(bidRequest) + .or(() -> extractAppDomain(bidRequest)) + .or(() -> extractDoohDomain(bidRequest)); + } + + public static Optional extractSiteDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getSite()).map(Site::getDomain); + } + + public static Optional extractAppDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getApp()).map(App::getDomain); + } + + public static Optional extractDoohDomain(BidRequest bidRequest) { + return Optional.ofNullable(bidRequest.getDooh()).map(Dooh::getDomain); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java new file mode 100644 index 00000000000..87506c4381b --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java @@ -0,0 +1,139 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Dooh; +import com.iab.openrtb.request.Publisher; +import com.iab.openrtb.request.Site; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DomainFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DomainFunction target = new DomainFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnSitePublisherDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .site(Site.builder().publisher(Publisher.builder().domain("domain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldReturnAppPublisherDomainWhenSiteHasNoDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .app(App.builder().publisher(Publisher.builder().domain("domain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldReturnDoohPublisherDomainWhenSiteAndAppHaveNoDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .dooh(Dooh.builder().publisher(Publisher.builder().domain("domain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldReturnSiteDomainWhenPublisherHasNoDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .site(Site.builder().domain("domain").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldReturnAppDomainWhenPublisherAndSiteHaveNoDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .app(App.builder().domain("domain").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldReturnDoohDomainWhenPublisherAndSiteAndAppHaveNoDomain() { + // given + final BidRequest bidRequest = BidRequest.builder() + .dooh(Dooh.builder().domain("domain").build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("domain"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenDomainIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + null); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java new file mode 100644 index 00000000000..d20dda4a6e9 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java @@ -0,0 +1,237 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Dooh; +import com.iab.openrtb.request.Publisher; +import com.iab.openrtb.request.Site; +import org.junit.jupiter.api.Test; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DomainInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final DomainInFunction target = new DomainInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'domains' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'domains' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("domains", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'domains' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode domainsNode = mapper.createArrayNode(); + domainsNode.add(TextNode.valueOf("test")); + domainsNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("domains", domainsNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'domains' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenSitePublisherDomainIsPresentInConfiguredDomains() { + // given + final BidRequest bidRequest = BidRequest.builder() + .site(Site.builder().publisher(Publisher.builder().domain("sitePubDomain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("sitePubDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenAppPublisherDomainIsPresentInConfiguredDomains() { + // given + final BidRequest bidRequest = BidRequest.builder() + .site(Site.builder().publisher(Publisher.builder().domain("sitePubDomain").build()).build()) + .app(App.builder().publisher(Publisher.builder().domain("appPubDomain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("appPubDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenDoohPublisherDomainIsPresentInConfiguredDomains() { + // given + final BidRequest bidRequest = BidRequest.builder() + .site(Site.builder().publisher(Publisher.builder().domain("sitePubDomain").build()).build()) + .app(App.builder().publisher(Publisher.builder().domain("appPubDomain").build()).build()) + .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("doohPubDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenSiteDomainIsPresentInConfiguredDomains() { + // given + final Site site = Site.builder() + .publisher(Publisher.builder().domain("sitePubDomain").build()) + .domain("siteDomain") + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .app(App.builder().publisher(Publisher.builder().domain("appPubDomain").build()).build()) + .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("siteDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenAppDomainIsPresentInConfiguredDomains() { + // given + final Site site = Site.builder() + .publisher(Publisher.builder().domain("sitePubDomain").build()) + .domain("siteDomain") + .build(); + + final App app = App.builder() + .publisher(Publisher.builder().domain("appPubDomain").build()) + .domain("appDomain") + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .app(app) + .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("appDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenDoohDomainIsPresentInConfiguredDomains() { + // given + final Site site = Site.builder() + .publisher(Publisher.builder().domain("sitePubDomain").build()) + .domain("siteDomain") + .build(); + + final App app = App.builder() + .publisher(Publisher.builder().domain("appPubDomain").build()) + .domain("appDomain") + .build(); + + final Dooh dooh = Dooh.builder() + .publisher(Publisher.builder().domain("doohPubDomain").build()) + .domain("doohDomain") + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .app(app) + .dooh(dooh) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("doohDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenAllSuppliedDomainsAreAbsentInConfiguredDomains() { + // given + final Site site = Site.builder() + .publisher(Publisher.builder().domain("sitePubDomain").build()) + .domain("siteDomain") + .build(); + + final App app = App.builder() + .publisher(Publisher.builder().domain("appPubDomain").build()) + .domain("appDomain") + .build(); + + final Dooh dooh = Dooh.builder() + .publisher(Publisher.builder().domain("doohPubDomain").build()) + .domain("doohDomain") + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .app(app) + .dooh(dooh) + .build(); + + final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( + RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), + givenConfigWithDomains("expectedDomain")); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private ObjectNode givenConfigWithDomains(String... domains) { + final ArrayNode domainsNode = mapper.createArrayNode(); + Arrays.stream(domains).map(TextNode::valueOf).forEach(domainsNode::add); + return mapper.createObjectNode().set("domains", domainsNode); + } +} From f365d408995a00bb7475259f8e5d31cc9c33d718 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 05:05:49 +0300 Subject: [PATCH 57/94] Major refactoring. --- .../PbRuleEngineModuleConfiguration.java | 23 +++---- .../core/config/AccountConfigParser.java | 9 +-- .../engine/core/config/StageConfigParser.java | 54 ++++++++-------- .../core/request/PerImpMatchingRule.java | 30 +++++++++ .../core/request/RequestMatchingRule.java | 61 +++++-------------- .../request/RequestMatchingRuleFactory.java | 30 +++++++++ ...ltContext.java => RequestRuleContext.java} | 7 ++- .../core/request/RequestSpecification.java | 16 +++-- .../request/context/RequestSchemaContext.java | 15 ----- .../functions/filter/AnalyticsMapper.java | 4 +- .../filter/FilterBiddersFunction.java | 8 +-- .../result/functions/filter/FilterUtils.java | 1 - .../result/functions/log/AnalyticsMapper.java | 4 +- .../result/functions/log/LogATagFunction.java | 6 +- .../schema/functions/AdUnitCodeFunction.java | 12 ++-- .../functions/AdUnitCodeInFunction.java | 12 ++-- .../schema/functions/BundleFunction.java | 9 +-- .../schema/functions/BundleInFunction.java | 9 +-- .../schema/functions/ChannelFunction.java | 8 +-- .../schema/functions/DataCenterFunction.java | 9 +-- .../functions/DataCenterInFunction.java | 9 +-- .../functions/DeviceCountryFunction.java | 8 +-- .../functions/DeviceCountryInFunction.java | 8 +-- .../schema/functions/DeviceTypeFunction.java | 9 +-- .../functions/DeviceTypeInFunction.java | 9 +-- .../schema/functions/DomainFunction.java | 9 +-- .../schema/functions/DomainInFunction.java | 8 +-- .../functions/EidAvailableFunction.java | 8 +-- .../schema/functions/EidInFunction.java | 8 +-- .../functions/FpdAvailableFunction.java | 8 +-- .../functions/GppSidAvailableFunction.java | 8 +-- .../schema/functions/GppSidInFunction.java | 8 +-- .../schema/functions/MediaTypeInFunction.java | 13 ++-- .../schema/functions/PrebidKeyFunction.java | 9 +-- .../schema/functions/TcfInScopeFunction.java | 8 +-- .../functions/UserFpdAvailableFunction.java | 8 +-- .../core/rules/MatchingRuleFactory.java | 10 +-- .../rule/engine/core/rules/PerStageRule.java | 4 +- .../engine/core/rules/StageSpecification.java | 6 +- .../rule/engine/core/rules/schema/Schema.java | 4 +- .../core/rules/schema/SchemaFunction.java | 4 +- .../rules/schema/SchemaFunctionArguments.java | 4 +- .../rules/schema/SchemaFunctionHolder.java | 4 +- .../schema/functions/PercentFunction.java | 4 +- .../rule/engine/v1/PbRuleEngineModule.java | 4 +- ...RuleEngineProcessedAuctionRequestHook.java | 8 ++- .../functions/AdUnitCodeFunctionTest.java | 32 +++++----- .../functions/AdUnitCodeInFunctionTest.java | 38 +++++++----- .../schema/functions/BundleFunctionTest.java | 20 +++--- .../functions/BundleInFunctionTest.java | 23 ++++--- .../schema/functions/ChannelFunctionTest.java | 24 +++++--- .../functions/DataCenterFunctionTest.java | 23 ++++--- .../functions/DataCenterInFunctionTest.java | 23 ++++--- .../functions/DeviceCountryFunctionTest.java | 23 ++++--- .../DeviceCountryInFunctionTest.java | 23 ++++--- .../functions/DeviceTypeFunctionTest.java | 20 +++--- .../functions/DeviceTypeInFunctionTest.java | 23 ++++--- .../schema/functions/DomainFunctionTest.java | 41 ++++++------- .../functions/DomainInFunctionTest.java | 48 ++++++++------- 59 files changed, 483 insertions(+), 395 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java create mode 100644 extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{context/RequestResultContext.java => RequestRuleContext.java} (66%) delete mode 100644 extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java index 61436f2a0cc..20aa6c9dc19 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java @@ -8,10 +8,9 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.v1.PbRuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.beans.factory.annotation.Value; @@ -28,29 +27,27 @@ public class PbRuleEngineModuleConfiguration { @Bean - PbRuleEngineModule ruleEngineModule(RuleParser ruleParser) { - return new PbRuleEngineModule(ruleParser); + PbRuleEngineModule ruleEngineModule(RuleParser ruleParser, + @Value("${datacenter-region:#{null}}") String datacenter) { + + return new PbRuleEngineModule(ruleParser, datacenter); } @Bean - StageConfigParser processedAuctionRequestStageParser( - BidderCatalog bidderCatalog, - @Value("${datacenter-region:#{null}}") String datacenterRegion) { - + StageConfigParser processedAuctionRequestStageParser( + BidderCatalog bidderCatalog) { final RandomGenerator randomGenerator = () -> ThreadLocalRandom.current().nextLong(); return new StageConfigParser<>( randomGenerator, Stage.processed_auction_request, new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), - (schema, ruleTree, analyticsKey, modelVersion) -> - new RequestMatchingRule(schema, ruleTree, modelVersion, analyticsKey, datacenterRegion)); + new RequestMatchingRuleFactory()); } @Bean AccountConfigParser accountConfigParser( - StageConfigParser< - RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser) { + StageConfigParser processedAuctionRequestStageParser) { return new AccountConfigParser(ObjectMapperProvider.mapper(), processedAuctionRequestStageParser); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java index 149e7b2ac4c..6f542881c66 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParser.java @@ -6,8 +6,7 @@ import com.iab.openrtb.request.BidRequest; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; @@ -16,13 +15,11 @@ public class AccountConfigParser { private final ObjectMapper mapper; - private final StageConfigParser< - RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser; + private final StageConfigParser processedAuctionRequestStageParser; public AccountConfigParser( ObjectMapper mapper, - StageConfigParser< - RequestSchemaContext, BidRequest, RequestResultContext> processedAuctionRequestStageParser) { + StageConfigParser processedAuctionRequestStageParser) { this.mapper = Objects.requireNonNull(mapper); this.processedAuctionRequestStageParser = Objects.requireNonNull(processedAuctionRequestStageParser); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 33f1cd94452..f7b069d7547 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -31,17 +31,17 @@ import java.util.Objects; import java.util.random.RandomGenerator; -public class StageConfigParser { +public class StageConfigParser { private final RandomGenerator randomGenerator; - private final StageSpecification specification; + private final StageSpecification specification; private final Stage stage; - private final MatchingRuleFactory matchingRuleFactory; + private final MatchingRuleFactory matchingRuleFactory; public StageConfigParser(RandomGenerator randomGenerator, Stage stage, - StageSpecification specification, - MatchingRuleFactory matchingRuleFactory) { + StageSpecification specification, + MatchingRuleFactory matchingRuleFactory) { this.randomGenerator = Objects.requireNonNull(randomGenerator); this.stage = Objects.requireNonNull(stage); @@ -49,8 +49,8 @@ public StageConfigParser(RandomGenerator randomGenerator, this.matchingRuleFactory = Objects.requireNonNull(matchingRuleFactory); } - public Rule parse(AccountConfig config) { - final List> stageSubrules = config.getRuleSets().stream() + public Rule parse(AccountConfig config) { + final List> stageSubrules = config.getRuleSets().stream() .filter(ruleSet -> stage.equals(ruleSet.getStage())) .filter(RuleSetConfig::isEnabled) .map(RuleSetConfig::getModelGroups) @@ -62,22 +62,22 @@ public Rule parse(AccountConfig config) { : CompositeRule.of(stageSubrules); } - private Rule parseModelGroupConfigs(List modelGroupConfigs) { - final List>> weightedRules = modelGroupConfigs.stream() + private Rule parseModelGroupConfigs(List modelGroupConfigs) { + final List>> weightedRules = modelGroupConfigs.stream() .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); return new RandomWeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); } - private Rule parseModelGroupConfig(ModelGroupConfig config) { - final Rule matchingRule = parseMatchingRule(config); - final Rule defaultRule = parseDefaultActionRule(config); + private Rule parseModelGroupConfig(ModelGroupConfig config) { + final Rule matchingRule = parseMatchingRule(config); + final Rule defaultRule = parseDefaultActionRule(config); return combineRules(matchingRule, defaultRule); } - private Rule parseMatchingRule(ModelGroupConfig config) { + private Rule parseMatchingRule(ModelGroupConfig config) { final List schemaConfig = config.getSchema(); final List rulesConfig = config.getRules(); @@ -85,12 +85,12 @@ private Rule parseMatchingRule(ModelGroupConfig config) { return null; } - final Schema schema = parseSchema(schemaConfig); + final Schema schema = parseSchema(schemaConfig); - final List> rules = rulesConfig.stream() + final List> rules = rulesConfig.stream() .map(this::parseRuleConfig) .toList(); - final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); + final RuleTree> ruleTree = RuleTreeFactory.buildTree(rules); if (schemaConfig.size() != ruleTree.getDepth()) { throw new InvalidMatcherConfiguration("Schema functions count and rules matchers count mismatch"); @@ -99,8 +99,8 @@ private Rule parseMatchingRule(ModelGroupConfig config) { return matchingRuleFactory.create(schema, ruleTree, config.getAnalyticsKey(), config.getVersion()); } - private Schema parseSchema(List schema) { - final List> schemaFunctions = schema.stream() + private Schema parseSchema(List schema) { + final List> schemaFunctions = schema.stream() .map(config -> SchemaFunctionHolder.of( config.getFunction(), specification.schemaFunctionByName(config.getFunction()), @@ -112,7 +112,7 @@ private Schema parseSchema(List schema) { return Schema.of(schemaFunctions); } - private void validateFunctionConfig(SchemaFunctionHolder holder) { + private void validateFunctionConfig(SchemaFunctionHolder holder) { try { holder.getSchemaFunction().validateConfig(holder.getConfig()); } catch (ConfigurationValidationException exception) { @@ -121,14 +121,14 @@ private void validateFunctionConfig(SchemaFunctionHolder holder) } } - private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { + private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); - final List> actions = parseActions(ruleConfig.getResults()); + final List> actions = parseActions(ruleConfig.getResults()); return RuleConfig.of(ruleFired, actions); } - private Rule parseDefaultActionRule(ModelGroupConfig config) { + private Rule parseDefaultActionRule(ModelGroupConfig config) { final List defaultActionConfig = config.getDefaultAction(); if (CollectionUtils.isEmpty(config.getDefaultAction())) { @@ -139,8 +139,8 @@ private Rule parseDefaultActionRule(ModelGroupConfig conf parseActions(defaultActionConfig), config.getAnalyticsKey(), config.getVersion()); } - private List> parseActions(List functionConfigs) { - final List> actions = functionConfigs.stream() + private List> parseActions(List functionConfigs) { + final List> actions = functionConfigs.stream() .map(config -> RuleAction.of( config.getFunction(), specification.resultFunctionByName(config.getFunction()), @@ -152,7 +152,7 @@ private List> parseActions(List action) { + private void validateActionConfig(RuleAction action) { try { action.getFunction().validateConfig(action.getConfig()); } catch (ConfigurationValidationException exception) { @@ -161,8 +161,8 @@ private void validateActionConfig(RuleAction action) { } } - private Rule combineRules( - Rule left, Rule right) { + private Rule combineRules( + Rule left, Rule right) { if (left == null && right == null) { return NoOpRule.create(); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java new file mode 100644 index 00000000000..da68b48d679 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java @@ -0,0 +1,30 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; + +import java.util.Objects; + +public class PerImpMatchingRule implements Rule { + + private final RequestMatchingRule delegate; + + public PerImpMatchingRule(RequestMatchingRule delegate) { + this.delegate = Objects.requireNonNull(delegate); + } + + @Override + public RuleResult process(BidRequest value, RequestRuleContext context) { + return value.getImp().stream().reduce( + RuleResult.unaltered(value), + (result, imp) -> result.mergeWith( + delegate.process( + result.getUpdateResult().getValue(), + RequestRuleContext.of( + context.getAuctionContext(), + new Granularity.Imp(imp.getId()), + context.getDatacenter()))), + RuleResult::mergeWith); + } +} diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java index a7bc1a95109..6b1124e595f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java @@ -1,11 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; -import org.apache.commons.collections4.SetUtils; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -21,79 +17,52 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; -public class RequestMatchingRule implements Rule { +public class RequestMatchingRule implements Rule { - private final Schema schema; - private final Set schemaFunctionNames; - private final RuleTree> ruleTree; + private final Schema schema; + private final RuleTree> ruleTree; private final String modelVersion; private final String analyticsKey; - private final String datacenter; - public RequestMatchingRule(Schema schema, - RuleTree> ruleTree, + public RequestMatchingRule(Schema schema, + RuleTree> ruleTree, String modelVersion, - String analyticsKey, - String datacenter) { + String analyticsKey) { this.schema = Objects.requireNonNull(schema); - this.schemaFunctionNames = schema.getFunctions().stream() - .map(SchemaFunctionHolder::getName) - .collect(Collectors.toSet()); this.ruleTree = Objects.requireNonNull(ruleTree); this.modelVersion = StringUtils.defaultString(modelVersion); this.analyticsKey = StringUtils.defaultString(analyticsKey); - this.datacenter = StringUtils.defaultString(datacenter); } @Override - public RuleResult process(BidRequest bidRequest, RequestResultContext context) { - return SetUtils.intersection(schemaFunctionNames, RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS).isEmpty() - ? processRule(bidRequest, Granularity.Request.instance(), context.getAuctionContext()) - : processPerImpRule(bidRequest, context.getAuctionContext()); - } - - private RuleResult processPerImpRule(BidRequest bidRequest, AuctionContext context) { - return bidRequest.getImp().stream().reduce( - RuleResult.unaltered(bidRequest), - (result, imp) -> result.mergeWith( - processRule(result.getUpdateResult().getValue(), new Granularity.Imp(imp.getId()), context)), - RuleResult::mergeWith); - } - - private RuleResult processRule(BidRequest bidRequest, - Granularity granularity, - AuctionContext auctionContext) { - - final RequestSchemaContext schemaFunctionContext = RequestSchemaContext.of(bidRequest, granularity, datacenter); - - final List> schemaFunctions = schema.getFunctions(); + public RuleResult process(BidRequest bidRequest, RequestRuleContext context) { + final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(schemaFunctionContext, holder.getConfig()))) + SchemaFunctionArguments.of(bidRequest, holder.getConfig(), context))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) .toList(); - final LookupResult> lookupResult; + final LookupResult> lookupResult; try { lookupResult = ruleTree.lookup(matchers); } catch (NoMatchingRuleException e) { return RuleResult.unaltered(bidRequest); } - final RuleConfig ruleConfig = lookupResult.getValue(); + final RuleConfig ruleConfig = lookupResult.getValue(); - final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() - .context(RequestResultContext.of(auctionContext, granularity)) + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) .schemaFunctionResults(mergeWithSchema(schema, matchers)) .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) .ruleFired(ruleConfig.getCondition()) @@ -112,7 +81,7 @@ private RuleResult processRule(BidRequest bidRequest, RuleResult::mergeWith); } - private static Map mergeWithSchema(Schema schema, List values) { + private static Map mergeWithSchema(Schema schema, List values) { return IntStream.range(0, values.size()) .boxed() .collect(Collectors.toMap( diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java new file mode 100644 index 00000000000..21091057c67 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java @@ -0,0 +1,30 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; + +public class RequestMatchingRuleFactory implements MatchingRuleFactory { + + @Override + public Rule create( + Schema schema, + RuleTree> ruleTree, + String analyticsKey, + String modelVersion) { + + final RequestMatchingRule requestMatchingRule = new RequestMatchingRule( + schema, ruleTree, analyticsKey, modelVersion); + + return schema.getFunctions().stream() + .map(SchemaFunctionHolder::getName) + .anyMatch(RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS::contains) + + ? new PerImpMatchingRule(requestMatchingRule) + : requestMatchingRule; + } +} diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java similarity index 66% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java index 6a411efe43b..35ea4e1f6b0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestResultContext.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestRuleContext.java @@ -1,13 +1,14 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.context; +package org.prebid.server.hooks.modules.rule.engine.core.request; import lombok.Value; import org.prebid.server.auction.model.AuctionContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; @Value(staticConstructor = "of") -public class RequestResultContext { +public class RequestRuleContext { AuctionContext auctionContext; Granularity granularity; + + String datacenter; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index 2ecc180c5e4..d157a95cdfb 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -3,8 +3,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.iab.openrtb.request.BidRequest; import org.prebid.server.bidder.BidderCatalog; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log.LogATagFunction; @@ -43,13 +41,13 @@ import java.util.random.RandomGenerator; public class RequestSpecification implements - StageSpecification { + StageSpecification { public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME, AdUnitCodeInFunction.NAME, MediaTypeInFunction.NAME); - private final Map> schemaFunctions; - private final Map> resultFunctions; + private final Map> schemaFunctions; + private final Map> resultFunctions; public RequestSpecification(ObjectMapper mapper, BidderCatalog bidderCatalog, @@ -86,8 +84,8 @@ ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), LogATagFunction.NAME, new LogATagFunction(mapper)); } - public SchemaFunction schemaFunctionByName(String name) { - final SchemaFunction function = schemaFunctions.get(name); + public SchemaFunction schemaFunctionByName(String name) { + final SchemaFunction function = schemaFunctions.get(name); if (function == null) { throw new InvalidSchemaFunctionException(name); } @@ -95,8 +93,8 @@ public SchemaFunction schemaFunctionByName(String name) { return function; } - public ResultFunction resultFunctionByName(String name) { - final ResultFunction function = resultFunctions.get(name); + public ResultFunction resultFunctionByName(String name) { + final ResultFunction function = resultFunctions.get(name); if (function == null) { throw new InvalidResultFunctionException(name); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java deleted file mode 100644 index 64fdef74634..00000000000 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/context/RequestSchemaContext.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request.context; - -import com.iab.openrtb.request.BidRequest; -import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; - -@Value(staticConstructor = "of") -public class RequestSchemaContext { - - BidRequest bidRequest; - - Granularity granularity; - - String datacenter; -} diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java index 0d21e8d04d9..7a54bbf23b0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -9,7 +9,7 @@ import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.v1.analytics.Result; import org.prebid.server.hooks.v1.analytics.Tags; @@ -31,7 +31,7 @@ private AnalyticsMapper() { public static Tags toTags(ObjectMapper mapper, String functionName, List seatNonBids, - InfrastructureArguments infrastructureArguments, + InfrastructureArguments infrastructureArguments, String analyticsValue) { final String analyticsKey = infrastructureArguments.getAnalyticsKey(); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 42ddcc477d9..a40c28153a8 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -11,7 +11,7 @@ import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.Set; -public abstract class FilterBiddersFunction implements ResultFunction { +public abstract class FilterBiddersFunction implements ResultFunction { private final ObjectMapper mapper; protected final BidderCatalog bidderCatalog; @@ -40,11 +40,11 @@ public FilterBiddersFunction(ObjectMapper mapper, BidderCatalog bidderCatalog) { } @Override - public RuleResult apply(ResultFunctionArguments arguments) { + public RuleResult apply(ResultFunctionArguments arguments) { final FilterBiddersFunctionConfig config = parseConfig(arguments.getConfig()); final BidRequest bidRequest = arguments.getOperand(); - final InfrastructureArguments infrastructureArguments = + final InfrastructureArguments infrastructureArguments = arguments.getInfrastructureArguments(); final UidsCookie uidsCookie = infrastructureArguments.getContext().getAuctionContext().getUidsCookie(); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java index 8d2d8eda477..8514f74ff35 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterUtils.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.prebid.server.util.StreamUtil; import java.util.Optional; import java.util.stream.Stream; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java index 5997c7a0c8c..8cde93a47f9 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java @@ -8,7 +8,7 @@ import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.v1.analytics.Result; import org.prebid.server.hooks.v1.analytics.Tags; @@ -25,7 +25,7 @@ private AnalyticsMapper() { } public static Tags toTags(ObjectMapper mapper, - InfrastructureArguments infrastructureArguments, + InfrastructureArguments infrastructureArguments, String analyticsValue) { final String analyticsKey = infrastructureArguments.getAnalyticsKey(); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java index d2f40df2294..504893d9b35 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.Objects; -public class LogATagFunction implements ResultFunction { +public class LogATagFunction implements ResultFunction { public static final String NAME = "logAtag"; @@ -27,7 +27,7 @@ public LogATagFunction(ObjectMapper mapper) { } @Override - public RuleResult apply(ResultFunctionArguments arguments) { + public RuleResult apply(ResultFunctionArguments arguments) { final Tags tags = AnalyticsMapper.toTags( mapper, arguments.getInfrastructureArguments(), diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java index 37a12a4c85e..165293f8b71 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunction.java @@ -5,21 +5,21 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class AdUnitCodeFunction implements SchemaFunction { +public class AdUnitCodeFunction implements SchemaFunction { public static final String NAME = "adUnitCode"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestSchemaContext context = arguments.getOperand(); - final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); - final BidRequest bidRequest = context.getBidRequest(); + public String extract(SchemaFunctionArguments arguments) { + final RequestRuleContext context = arguments.getContext(); + final String impId = ((Granularity.Imp) context.getGranularity()).impId(); + final BidRequest bidRequest = arguments.getOperand(); return ListUtils.emptyIfNull(bidRequest.getImp()).stream() .filter(imp -> StringUtils.equals(imp.getId(), impId)) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java index 3b4ab2faf9b..6b015863f97 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunction.java @@ -7,7 +7,7 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.AdUnitCodeUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; @@ -19,17 +19,17 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class AdUnitCodeInFunction implements SchemaFunction { +public class AdUnitCodeInFunction implements SchemaFunction { public static final String NAME = "adUnitCodeIn"; private static final String CODES_FIELD = "codes"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestSchemaContext context = arguments.getOperand(); - final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); - final BidRequest bidRequest = context.getBidRequest(); + public String extract(SchemaFunctionArguments arguments) { + final RequestRuleContext context = arguments.getContext(); + final String impId = ((Granularity.Imp) context.getGranularity()).impId(); + final BidRequest bidRequest = arguments.getOperand(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() .filter(imp -> StringUtils.equals(imp.getId(), impId)) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java index b98220f57ec..c99f0b618a0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunction.java @@ -2,20 +2,21 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class BundleFunction implements SchemaFunction { +public class BundleFunction implements SchemaFunction { public static final String NAME = "bundle"; @Override - public String extract(SchemaFunctionArguments arguments) { - return Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) + public String extract(SchemaFunctionArguments arguments) { + return Optional.ofNullable(arguments.getOperand().getApp()) .map(App::getBundle) .orElse(UNDEFINED_RESULT); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java index ac907c7e745..9389d686938 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunction.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.App; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -11,15 +12,15 @@ import java.util.Optional; -public class BundleInFunction implements SchemaFunction { +public class BundleInFunction implements SchemaFunction { public static final String NAME = "bundleIn"; private static final String BUNDLES_FIELD = "bundles"; @Override - public String extract(SchemaFunctionArguments arguments) { - final String bundle = Optional.ofNullable(arguments.getOperand().getBidRequest().getApp()) + public String extract(SchemaFunctionArguments arguments) { + final String bundle = Optional.ofNullable(arguments.getOperand().getApp()) .map(App::getBundle) .orElse(UNDEFINED_RESULT); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java index 098c2125bd6..2c8a5fd94aa 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunction.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,13 +12,13 @@ import java.util.Optional; -public class ChannelFunction implements SchemaFunction { +public class ChannelFunction implements SchemaFunction { public static final String NAME = "channel"; @Override - public String extract(SchemaFunctionArguments arguments) { - return Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + return Optional.of(arguments.getOperand()) .map(BidRequest::getExt) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getChannel) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java index 217535e516b..6d3e7acc7c1 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunction.java @@ -1,19 +1,20 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class DataCenterFunction implements SchemaFunction { +public class DataCenterFunction implements SchemaFunction { public static final String NAME = "dataCenter"; @Override - public String extract(SchemaFunctionArguments arguments) { - return StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); + public String extract(SchemaFunctionArguments arguments) { + return StringUtils.defaultIfEmpty(arguments.getContext().getDatacenter(), UNDEFINED_RESULT); } @Override diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java index 438c0bf1254..8679d9c70e3 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunction.java @@ -2,22 +2,23 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.util.StreamUtil; -public class DataCenterInFunction implements SchemaFunction { +public class DataCenterInFunction implements SchemaFunction { public static final String NAME = "dataCenterIn"; private static final String DATACENTERS_FIELD = "datacenters"; @Override - public String extract(SchemaFunctionArguments arguments) { - final String datacenter = StringUtils.defaultIfEmpty(arguments.getOperand().getDatacenter(), UNDEFINED_RESULT); + public String extract(SchemaFunctionArguments arguments) { + final String datacenter = StringUtils.defaultIfEmpty(arguments.getContext().getDatacenter(), UNDEFINED_RESULT); final boolean matches = StreamUtil.asStream(arguments.getConfig().get(DATACENTERS_FIELD).elements()) .map(JsonNode::asText) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java index 8c0215cb4a7..f2444f58985 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunction.java @@ -4,20 +4,20 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class DeviceCountryFunction implements SchemaFunction { +public class DeviceCountryFunction implements SchemaFunction { public static final String NAME = "deviceCountry"; @Override - public String extract(SchemaFunctionArguments arguments) { - return Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + return Optional.of(arguments.getOperand()) .map(BidRequest::getDevice) .map(Device::getGeo) .map(Geo::getCountry) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java index 42826146eae..d2dbf5904ef 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunction.java @@ -5,7 +5,7 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -13,14 +13,14 @@ import java.util.Optional; -public class DeviceCountryInFunction implements SchemaFunction { +public class DeviceCountryInFunction implements SchemaFunction { public static final String NAME = "deviceCountryIn"; private static final String COUNTRIES_FIELD = "countries"; @Override - public String extract(SchemaFunctionArguments arguments) { - final String deviceCountry = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final String deviceCountry = Optional.of(arguments.getOperand()) .map(BidRequest::getDevice) .map(Device::getGeo) .map(Geo::getCountry) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java index 8772d83fcd9..490db16a351 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunction.java @@ -1,21 +1,22 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class DeviceTypeFunction implements SchemaFunction { +public class DeviceTypeFunction implements SchemaFunction { public static final String NAME = "deviceType"; @Override - public String extract(SchemaFunctionArguments arguments) { - return Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) + public String extract(SchemaFunctionArguments arguments) { + return Optional.ofNullable(arguments.getOperand().getDevice()) .map(Device::getDevicetype) .map(String::valueOf) .orElse(UNDEFINED_RESULT); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java index 8c9d7172322..daacb7ad434 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunction.java @@ -2,8 +2,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -11,15 +12,15 @@ import java.util.Optional; -public class DeviceTypeInFunction implements SchemaFunction { +public class DeviceTypeInFunction implements SchemaFunction { public static final String NAME = "deviceTypeIn"; public static final String TYPES_FIELD = "types"; @Override - public String extract(SchemaFunctionArguments arguments) { - final Integer deviceType = Optional.ofNullable(arguments.getOperand().getBidRequest().getDevice()) + public String extract(SchemaFunctionArguments arguments) { + final Integer deviceType = Optional.ofNullable(arguments.getOperand().getDevice()) .map(Device::getDevicetype) .orElse(null); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java index fe8943febee..0fb1e39082f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunction.java @@ -1,19 +1,20 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.DomainUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; -public class DomainFunction implements SchemaFunction { +public class DomainFunction implements SchemaFunction { public static final String NAME = "domain"; @Override - public String extract(SchemaFunctionArguments arguments) { - return DomainUtils.extractDomain(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + return DomainUtils.extractDomain(arguments.getOperand()) .orElse(UNDEFINED_RESULT); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java index 7af83a62175..bfd07a13ea9 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.util.DomainUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; @@ -15,15 +15,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class DomainInFunction implements SchemaFunction { +public class DomainInFunction implements SchemaFunction { public static final String NAME = "domainIn"; private static final String DOMAINS_FIELD = "domains"; @Override - public String extract(SchemaFunctionArguments arguments) { - final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + public String extract(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand(); final Set suppliedDomains = Stream.of( DomainUtils.extractSitePublisherDomain(bidRequest), diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java index b694f8f7f25..3fa536bf966 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,13 +12,13 @@ import java.util.Optional; import java.util.function.Predicate; -public class EidAvailableFunction implements SchemaFunction { +public class EidAvailableFunction implements SchemaFunction { public static final String NAME = "eidAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { - final boolean available = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final boolean available = Optional.of(arguments.getOperand()) .map(BidRequest::getUser) .map(User::getEids) .filter(Predicate.not(List::isEmpty)) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java index 8d11f8f2691..6c9e4d0070f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunction.java @@ -5,7 +5,7 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Eid; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -16,15 +16,15 @@ import java.util.Set; import java.util.stream.Collectors; -public class EidInFunction implements SchemaFunction { +public class EidInFunction implements SchemaFunction { public static final String NAME = "eidIn"; private static final String SOURCES_FIELD = "sources"; @Override - public String extract(SchemaFunctionArguments arguments) { - final Set sources = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final Set sources = Optional.of(arguments.getOperand()) .map(BidRequest::getUser) .map(User::getEids) .stream() diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java index 55c3370ed96..9b6ec28d121 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java @@ -6,7 +6,7 @@ import com.iab.openrtb.request.Content; import com.iab.openrtb.request.Site; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -18,13 +18,13 @@ import java.util.Optional; import java.util.function.Predicate; -public class FpdAvailableFunction implements SchemaFunction { +public class FpdAvailableFunction implements SchemaFunction { public static final String NAME = "fpdAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { - final BidRequest bidRequest = arguments.getOperand().getBidRequest(); + public String extract(SchemaFunctionArguments arguments) { + final BidRequest bidRequest = arguments.getOperand(); final boolean available = isUserDataAvailable(bidRequest) || isUserExtDataAvailable(bidRequest) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java index 10b04c107f9..ef306e65351 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -12,13 +12,13 @@ import java.util.Objects; import java.util.Optional; -public class GppSidAvailableFunction implements SchemaFunction { +public class GppSidAvailableFunction implements SchemaFunction { public static final String NAME = "gppSidAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { - final boolean available = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final boolean available = Optional.of(arguments.getOperand()) .map(BidRequest::getRegs) .map(Regs::getGppSid) .stream() diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java index 992fde8bfb0..46bf0aad29c 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunction.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -15,15 +15,15 @@ import java.util.Set; import java.util.stream.Collectors; -public class GppSidInFunction implements SchemaFunction { +public class GppSidInFunction implements SchemaFunction { public static final String NAME = "gppSidIn"; private static final String SIDS_FIELD = "sids"; @Override - public String extract(SchemaFunctionArguments arguments) { - final Set sids = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final Set sids = Optional.of(arguments.getOperand()) .map(BidRequest::getRegs) .map(Regs::getGppSid) .stream() diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index e15c2b4b258..79d0074e893 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -7,7 +7,7 @@ import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -17,18 +17,17 @@ import java.util.HashSet; import java.util.Set; -public class MediaTypeInFunction implements SchemaFunction { +public class MediaTypeInFunction implements SchemaFunction { public static final String NAME = "mediaTypeIn"; private static final String TYPES_FIELD = "types"; @Override - public String extract(SchemaFunctionArguments arguments) { - final RequestSchemaContext context = arguments.getOperand(); - - final String impId = ((Granularity.Imp) arguments.getOperand().getGranularity()).impId(); - final BidRequest bidRequest = context.getBidRequest(); + public String extract(SchemaFunctionArguments arguments) { + final RequestRuleContext context = arguments.getContext(); + final String impId = ((Granularity.Imp) context.getGranularity()).impId(); + final BidRequest bidRequest = arguments.getOperand(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() .filter(imp -> StringUtils.equals(imp.getId(), impId)) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java index bb16f92c186..17c42beb5b5 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunction.java @@ -2,7 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -11,15 +12,15 @@ import java.util.Optional; -public class PrebidKeyFunction implements SchemaFunction { +public class PrebidKeyFunction implements SchemaFunction { public static final String NAME = "prebidKey"; private static final String KEY_FIELD = "key"; @Override - public String extract(SchemaFunctionArguments arguments) { - return Optional.ofNullable(arguments.getOperand().getBidRequest().getExt()) + public String extract(SchemaFunctionArguments arguments) { + return Optional.ofNullable(arguments.getOperand().getExt()) .map(ExtRequest::getPrebid) .map(ExtRequestPrebid::getKvps) .map(kvps -> kvps.get(arguments.getConfig().get(KEY_FIELD).asText())) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java index a9b513026ee..b98d9672a44 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunction.java @@ -3,20 +3,20 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Regs; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.Optional; -public class TcfInScopeFunction implements SchemaFunction { +public class TcfInScopeFunction implements SchemaFunction { public static final String NAME = "tcfInScope"; @Override - public String extract(SchemaFunctionArguments arguments) { - final boolean inScope = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final boolean inScope = Optional.of(arguments.getOperand()) .map(BidRequest::getRegs) .map(Regs::getGdpr) .filter(Integer.valueOf(1)::equals) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java index eb438a766e5..8872fd17674 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.User; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; @@ -13,13 +13,13 @@ import java.util.Optional; import java.util.function.Predicate; -public class UserFpdAvailableFunction implements SchemaFunction { +public class UserFpdAvailableFunction implements SchemaFunction { public static final String NAME = "userFpdAvailable"; @Override - public String extract(SchemaFunctionArguments arguments) { - final Optional user = Optional.of(arguments.getOperand().getBidRequest()) + public String extract(SchemaFunctionArguments arguments) { + final Optional user = Optional.of(arguments.getOperand()) .map(BidRequest::getUser); final boolean userDataAvailable = user.map(User::getData) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java index 66bfa2f0339..cb4fcde7aa0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java @@ -3,10 +3,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -public interface MatchingRuleFactory { +public interface MatchingRuleFactory { - Rule create(Schema schema, - RuleTree> ruleTree, - String analyticsKey, - String modelVersion); + Rule create(Schema schema, + RuleTree> ruleTree, + String analyticsKey, + String modelVersion); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index 63a0a0e9418..4fbb56573c4 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -4,7 +4,7 @@ import lombok.Builder; import lombok.Value; import lombok.experimental.Accessors; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import java.time.Instant; @@ -15,6 +15,6 @@ public class PerStageRule { Instant timestamp; - Rule processedAuctionRequestRule; + Rule processedAuctionRequestRule; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java index d353a79a828..2083983091f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/StageSpecification.java @@ -3,9 +3,9 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; -public interface StageSpecification { +public interface StageSpecification { - SchemaFunction schemaFunctionByName(String name); + SchemaFunction schemaFunctionByName(String name); - ResultFunction resultFunctionByName(String name); + ResultFunction resultFunctionByName(String name); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java index 67172405a59..5c725f23e77 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -6,7 +6,7 @@ import java.util.Set; @Value(staticConstructor = "of") -public class Schema { +public class Schema { - List> functions; + List> functions; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java index 03641526a93..887d76eb9ec 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunction.java @@ -2,11 +2,11 @@ import com.fasterxml.jackson.databind.node.ObjectNode; -public interface SchemaFunction { +public interface SchemaFunction { String UNDEFINED_RESULT = "undefined"; - String extract(SchemaFunctionArguments arguments); + String extract(SchemaFunctionArguments arguments); void validateConfig(ObjectNode config); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java index 122afba582c..062d1776d0f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionArguments.java @@ -4,9 +4,11 @@ import lombok.Value; @Value(staticConstructor = "of") -public class SchemaFunctionArguments { +public class SchemaFunctionArguments { T operand; ObjectNode config; + + C context; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java index caaa317b47a..2264cfc2088 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/SchemaFunctionHolder.java @@ -4,11 +4,11 @@ import lombok.Value; @Value(staticConstructor = "of") -public class SchemaFunctionHolder { +public class SchemaFunctionHolder { String name; - SchemaFunction schemaFunction; + SchemaFunction schemaFunction; ObjectNode config; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java index abf6163b5d0..2d03ce5e6e0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/functions/PercentFunction.java @@ -9,7 +9,7 @@ import java.util.random.RandomGenerator; @RequiredArgsConstructor -public class PercentFunction implements SchemaFunction { +public class PercentFunction implements SchemaFunction { public static final String NAME = "percent"; @@ -18,7 +18,7 @@ public class PercentFunction implements SchemaFunction { private final RandomGenerator random; @Override - public String extract(SchemaFunctionArguments arguments) { + public String extract(SchemaFunctionArguments arguments) { final int resolvedUpperBound = Math.min(Math.max(arguments.getConfig().get(PCT_FIELD).asInt(), 0), 100); return Boolean.toString(random.nextInt(100) < resolvedUpperBound); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java index 01543b7f5ed..7ca7206116f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineModule.java @@ -14,9 +14,9 @@ public class PbRuleEngineModule implements Module { private final Collection> hooks; - public PbRuleEngineModule(RuleParser ruleParser) { + public PbRuleEngineModule(RuleParser ruleParser, String datacenter) { this.hooks = Collections.singleton( - new PbRuleEngineProcessedAuctionRequestHook(ruleParser)); + new PbRuleEngineProcessedAuctionRequestHook(ruleParser, datacenter)); } @Override diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index bc70c234403..a0552f22b0c 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -9,7 +9,7 @@ import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.InvocationAction; @@ -27,9 +27,11 @@ public class PbRuleEngineProcessedAuctionRequestHook implements ProcessedAuction private static final String CODE = "pb-rule-engine-processed-auction-request"; private final RuleParser ruleParser; + private final String datacenter; - public PbRuleEngineProcessedAuctionRequestHook(RuleParser ruleParser) { + public PbRuleEngineProcessedAuctionRequestHook(RuleParser ruleParser, String datacenter) { this.ruleParser = Objects.requireNonNull(ruleParser); + this.datacenter = Objects.requireNonNull(datacenter); } @Override @@ -48,7 +50,7 @@ public Future> call(AuctionRequestPayloa return ruleParser.parseForAccount(accountId, accountConfig) .map(PerStageRule::processedAuctionRequestRule) .map(rule -> rule.process( - bidRequest, RequestResultContext.of(context, Granularity.Request.instance()))) + bidRequest, RequestRuleContext.of(context, Granularity.Request.instance(), datacenter))) .flatMap(PbRuleEngineProcessedAuctionRequestHook::succeeded) .recover(PbRuleEngineProcessedAuctionRequestHook::failure); } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java index 5dd25198789..97873c845c8 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java @@ -6,8 +6,9 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -42,9 +43,7 @@ public void extractShouldReturnGpidWhenPresent() { final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("gpid"); @@ -60,9 +59,7 @@ public void extractShouldReturnTagidWhenGpidAbsentAndTagidPresent() { final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("tagId"); @@ -78,9 +75,7 @@ public void extractShouldReturnPbAdSlotWhenGpidAndTagidAreAbsent() { final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("pbadslot"); @@ -98,9 +93,7 @@ public void extractShouldReturnStoredRequestIdWhenGpidAndTagidAndPbAdSlotAreAbse final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("srid"); @@ -111,11 +104,18 @@ public void extractShouldFallbackToUndefinedWhenAllAdUnitCodeSourcesAreAbsent() // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), new Granularity.Imp("impId"), "datacenter")); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java index 09e8bd63082..3fe896539ee 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java @@ -8,8 +8,9 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -76,9 +77,8 @@ public void extractShouldReturnTrueWhenGpidPresentInConfiguredCodes() { final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - givenConfigWithCodes("gpid")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "gpid"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -95,9 +95,8 @@ public void extractShouldReturnTrueWhenTagidPresentAndGpidIsAbsentInConfiguredCo final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - givenConfigWithCodes("tagId")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "tagId"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -118,9 +117,8 @@ public void extractShouldReturnTrueWhenPbAdSlotPresentAndGpidAndTagidAreAbsentIn final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - givenConfigWithCodes("pbadslot")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "pbadslot"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -144,9 +142,8 @@ public void extractShouldReturnTrueWhenSridPresentAndGpidAndTagidAndPbAdSlotAreA final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - givenConfigWithCodes("srid")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "srid"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -170,15 +167,24 @@ public void extractShouldReturnFalseWhenAdUnitCodesDoesNotMatchConfiguredCodes() final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, new Granularity.Imp("impId"), "datacenter"), - givenConfigWithCodes("adUnitCode")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "adUnitCode"); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String... codes) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithCodes(codes), + RequestRuleContext.of(AuctionContext.builder().build(), new Granularity.Imp("impId"), "datacenter")); + } + private ObjectNode givenConfigWithCodes(String... codes) { final ArrayNode codesNode = mapper.createArrayNode(); Arrays.stream(codes).map(TextNode::valueOf).forEach(codesNode::add); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java index 72686483cf1..ad62d55c811 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java @@ -6,8 +6,9 @@ import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -38,9 +39,7 @@ public void extractShouldReturnBundle() { .app(App.builder().bundle("bundle").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("bundle"); @@ -51,11 +50,18 @@ public void extractShouldFallbackToUndefinedWhenBundleIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java index ceda9b2e550..8a71982c30c 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java @@ -8,8 +8,9 @@ import com.iab.openrtb.request.App; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -72,9 +73,8 @@ public void extractShouldReturnTrueWhenBundlePresentInConfiguredBundles() { .app(App.builder().bundle("bundle").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithBundles("bundle")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "bundle"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -87,14 +87,23 @@ public void extractShouldReturnFalseWhenBundleIsAbsentInConfiguredBundles() { .app(App.builder().bundle("bundle").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithBundles("expectedBundle")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "expectedBundle"); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String... bundles) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithBundles(bundles), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + private ObjectNode givenConfigWithBundles(String... bundles) { final ArrayNode bundlesNode = mapper.createArrayNode(); Arrays.stream(bundles).map(TextNode::valueOf).forEach(bundlesNode::add); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java index 70f3236e7b7..75fb617323f 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java @@ -5,8 +5,9 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; import org.prebid.server.proto.openrtb.ext.request.ExtRequest; @@ -43,9 +44,7 @@ public void extractShouldReturnChannel() { final BidRequest bidRequest = BidRequest.builder().ext(ext).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("channel"); @@ -61,9 +60,7 @@ public void extractShouldReturnWebWhenChannelIsPbjs() { final BidRequest bidRequest = BidRequest.builder().ext(ext).build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("web"); @@ -74,11 +71,18 @@ public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java index 5f835c0effc..1b4a43baa6b 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java @@ -5,8 +5,9 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -35,9 +36,8 @@ public void extractShouldReturnDataCenter() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "datacenter"); // when and then assertThat(target.extract(arguments)).isEqualTo("datacenter"); @@ -48,11 +48,20 @@ public void extractShouldFallbackToUndefinedWhenDataCenterIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), null), - null); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, null); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String dataCenter) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), dataCenter)); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java index 0599b3f7326..b639c0f2f59 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java @@ -7,8 +7,9 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -69,9 +70,8 @@ public void extractShouldReturnTrueWhenDataCenterPresentInConfiguredDatacenters( // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDataCenters("datacenter")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "datacenter", "datacenter"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -82,14 +82,23 @@ public void extractShouldReturnFalseWhenBundleIsAbsentInConfiguredBundles() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDataCenters("expectedDatacenter")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "datacenter", "expectedDatacenter"); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String datacenter, + String... expectedDatacenters) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithDataCenters(expectedDatacenters), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), datacenter)); + } private ObjectNode givenConfigWithDataCenters(String... dataCenters) { final ArrayNode dataCentersNode = mapper.createArrayNode(); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java index 631aa2e9fdb..ef5508cd7f7 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java @@ -7,13 +7,11 @@ import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; -import org.prebid.server.proto.openrtb.ext.request.ExtRequest; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; -import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidChannel; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -42,9 +40,7 @@ public void extractShouldReturnDeviceCountry() { .device(Device.builder().geo(Geo.builder().country("country").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("country"); @@ -55,11 +51,18 @@ public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java index f8d1df9f3c4..491f3e11c34 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java @@ -9,8 +9,9 @@ import com.iab.openrtb.request.Device; import com.iab.openrtb.request.Geo; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -73,9 +74,8 @@ public void extractShouldReturnTrueWhenDeviceCountryPresentInConfiguredCountries .device(Device.builder().geo(Geo.builder().country("country").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithCountries("country")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "country"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -86,14 +86,23 @@ public void extractShouldReturnFalseWhenDeviceCountryIsAbsentInConfiguredCountri // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithCountries("expectedCountry")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "expectedCountry"); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String... countries) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithCountries(countries), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + private ObjectNode givenConfigWithCountries(String... countries) { final ArrayNode countriesNode = mapper.createArrayNode(); Arrays.stream(countries).map(TextNode::valueOf).forEach(countriesNode::add); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java index fd8a0a75a0f..d7f8849d4d0 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java @@ -6,8 +6,9 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -38,9 +39,7 @@ public void extractShouldReturnDeviceType() { .device(Device.builder().devicetype(12345).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("12345"); @@ -51,11 +50,18 @@ public void extractShouldFallbackToUndefinedWhenChannelIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java index e229f50b116..1c04d102eda 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java @@ -8,8 +8,9 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Device; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -72,9 +73,8 @@ public void extractShouldReturnTrueWhenDeviceTypePresentInConfiguredTypes() { .device(Device.builder().devicetype(12345).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithTypes(12345)); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, 12345); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -85,14 +85,23 @@ public void extractShouldReturnFalseWhenDeviceTypeIsAbsentInConfiguredTypes() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithTypes(1)); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, 1); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + int... types) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithTypes(types), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + private ObjectNode givenConfigWithTypes(int... types) { final ArrayNode typesNode = mapper.createArrayNode(); Arrays.stream(types).mapToObj(IntNode::valueOf).forEach(typesNode::add); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java index 87506c4381b..d1f41058a97 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java @@ -9,8 +9,9 @@ import com.iab.openrtb.request.Publisher; import com.iab.openrtb.request.Site; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -41,9 +42,7 @@ public void extractShouldReturnSitePublisherDomain() { .site(Site.builder().publisher(Publisher.builder().domain("domain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -56,9 +55,7 @@ public void extractShouldReturnAppPublisherDomainWhenSiteHasNoDomain() { .app(App.builder().publisher(Publisher.builder().domain("domain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -71,9 +68,7 @@ public void extractShouldReturnDoohPublisherDomainWhenSiteAndAppHaveNoDomain() { .dooh(Dooh.builder().publisher(Publisher.builder().domain("domain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -86,9 +81,7 @@ public void extractShouldReturnSiteDomainWhenPublisherHasNoDomain() { .site(Site.builder().domain("domain").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -101,9 +94,7 @@ public void extractShouldReturnAppDomainWhenPublisherAndSiteHaveNoDomain() { .app(App.builder().domain("domain").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -116,9 +107,7 @@ public void extractShouldReturnDoohDomainWhenPublisherAndSiteAndAppHaveNoDomain( .dooh(Dooh.builder().domain("domain").build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("domain"); @@ -129,11 +118,19 @@ public void extractShouldFallbackToUndefinedWhenDomainIsAbsent() { // given final BidRequest bidRequest = BidRequest.builder().build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - null); + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); // when and then assertThat(target.extract(arguments)).isEqualTo("undefined"); } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java index d20dda4a6e9..08946ea26cc 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java @@ -11,8 +11,9 @@ import com.iab.openrtb.request.Publisher; import com.iab.openrtb.request.Site; import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; -import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; @@ -75,9 +76,8 @@ public void extractShouldReturnTrueWhenSitePublisherDomainIsPresentInConfiguredD .site(Site.builder().publisher(Publisher.builder().domain("sitePubDomain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("sitePubDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "sitePubDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -91,9 +91,8 @@ public void extractShouldReturnTrueWhenAppPublisherDomainIsPresentInConfiguredDo .app(App.builder().publisher(Publisher.builder().domain("appPubDomain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("appPubDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "appPubDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -108,9 +107,8 @@ public void extractShouldReturnTrueWhenDoohPublisherDomainIsPresentInConfiguredD .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("doohPubDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "doohPubDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -130,9 +128,8 @@ public void extractShouldReturnTrueWhenSiteDomainIsPresentInConfiguredDomains() .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("siteDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "siteDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -157,9 +154,8 @@ public void extractShouldReturnTrueWhenAppDomainIsPresentInConfiguredDomains() { .dooh(Dooh.builder().publisher(Publisher.builder().domain("doohPubDomain").build()).build()) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("appDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "appDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -189,9 +185,8 @@ public void extractShouldReturnTrueWhenDoohDomainIsPresentInConfiguredDomains() .dooh(dooh) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("doohDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "doohDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("true"); @@ -221,14 +216,23 @@ public void extractShouldReturnFalseWhenAllSuppliedDomainsAreAbsentInConfiguredD .dooh(dooh) .build(); - final SchemaFunctionArguments arguments = SchemaFunctionArguments.of( - RequestSchemaContext.of(bidRequest, Granularity.Request.instance(), "datacenter"), - givenConfigWithDomains("expectedDomain")); + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "expectedDomain"); // when and then assertThat(target.extract(arguments)).isEqualTo("false"); } + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String... domains) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithDomains(domains), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + private ObjectNode givenConfigWithDomains(String... domains) { final ArrayNode domainsNode = mapper.createArrayNode(); Arrays.stream(domains).map(TextNode::valueOf).forEach(domainsNode::add); From 78bfdef04644124e4f8335b6b74dee7cb23124f6 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 05:14:15 +0300 Subject: [PATCH 58/94] Generified condition matching rule. --- .../core/request/PerImpMatchingRule.java | 5 ++- .../request/RequestMatchingRuleFactory.java | 3 +- .../ConditionMatchingRule.java} | 40 +++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/{request/RequestMatchingRule.java => rules/ConditionMatchingRule.java} (62%) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java index da68b48d679..2808df051b0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -8,9 +9,9 @@ public class PerImpMatchingRule implements Rule { - private final RequestMatchingRule delegate; + private final ConditionMatchingRule delegate; - public PerImpMatchingRule(RequestMatchingRule delegate) { + public PerImpMatchingRule(ConditionMatchingRule delegate) { this.delegate = Objects.requireNonNull(delegate); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java index 21091057c67..260b83ec0c9 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; @@ -17,7 +18,7 @@ public Rule create( String analyticsKey, String modelVersion) { - final RequestMatchingRule requestMatchingRule = new RequestMatchingRule( + final ConditionMatchingRule requestMatchingRule = new ConditionMatchingRule<>( schema, ruleTree, analyticsKey, modelVersion); return schema.getFunctions().stream() diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java similarity index 62% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java index 6b1124e595f..bd762b1815e 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java @@ -1,10 +1,6 @@ -package org.prebid.server.hooks.modules.rule.engine.core.request; +package org.prebid.server.hooks.modules.rule.engine.core.rules; -import com.iab.openrtb.request.BidRequest; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; @@ -22,18 +18,18 @@ import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; -public class RequestMatchingRule implements Rule { +public class ConditionMatchingRule implements Rule { - private final Schema schema; - private final RuleTree> ruleTree; + private final Schema schema; + private final RuleTree> ruleTree; private final String modelVersion; private final String analyticsKey; - public RequestMatchingRule(Schema schema, - RuleTree> ruleTree, - String modelVersion, - String analyticsKey) { + public ConditionMatchingRule(Schema schema, + RuleTree> ruleTree, + String modelVersion, + String analyticsKey) { this.schema = Objects.requireNonNull(schema); @@ -43,25 +39,25 @@ public RequestMatchingRule(Schema schema, } @Override - public RuleResult process(BidRequest bidRequest, RequestRuleContext context) { - final List> schemaFunctions = schema.getFunctions(); + public RuleResult process(T value, C context) { + final List> schemaFunctions = schema.getFunctions(); final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( - SchemaFunctionArguments.of(bidRequest, holder.getConfig(), context))) + SchemaFunctionArguments.of(value, holder.getConfig(), context))) .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) .toList(); - final LookupResult> lookupResult; + final LookupResult> lookupResult; try { lookupResult = ruleTree.lookup(matchers); } catch (NoMatchingRuleException e) { - return RuleResult.unaltered(bidRequest); + return RuleResult.unaltered(value); } - final RuleConfig ruleConfig = lookupResult.getValue(); + final RuleConfig ruleConfig = lookupResult.getValue(); - final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() .context(context) .schemaFunctionResults(mergeWithSchema(schema, matchers)) .schemaFunctionMatches(mergeWithSchema(schema, lookupResult.getMatches())) @@ -71,7 +67,7 @@ public RuleResult process(BidRequest bidRequest, RequestRuleContext .build(); return ruleConfig.getActions().stream().reduce( - RuleResult.unaltered(bidRequest), + RuleResult.unaltered(value), (result, action) -> result.mergeWith( action.getFunction().apply( ResultFunctionArguments.of( @@ -81,7 +77,7 @@ public RuleResult process(BidRequest bidRequest, RequestRuleContext RuleResult::mergeWith); } - private static Map mergeWithSchema(Schema schema, List values) { + private Map mergeWithSchema(Schema schema, List values) { return IntStream.range(0, values.size()) .boxed() .collect(Collectors.toMap( From f93ec2a4bb7ab894c0e761cc0bff1bfb3b6381b5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 05:16:21 +0300 Subject: [PATCH 59/94] Minor refactorings. --- .../engine/config/PbRuleEngineModuleConfiguration.java | 4 ++-- .../engine/core/request/RequestMatchingRuleFactory.java | 2 +- ...Specification.java => RequestStageSpecification.java} | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{RequestSpecification.java => RequestStageSpecification.java} (95%) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java index 20aa6c9dc19..fe99de6a98f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java @@ -10,7 +10,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestSpecification; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestStageSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.PbRuleEngineModule; import org.prebid.server.json.ObjectMapperProvider; import org.springframework.beans.factory.annotation.Value; @@ -41,7 +41,7 @@ StageConfigParser processedAuctionRequestStagePa return new StageConfigParser<>( randomGenerator, Stage.processed_auction_request, - new RequestSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), + new RequestStageSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), new RequestMatchingRuleFactory()); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java index 260b83ec0c9..25ccecec765 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java @@ -23,7 +23,7 @@ public Rule create( return schema.getFunctions().stream() .map(SchemaFunctionHolder::getName) - .anyMatch(RequestSpecification.PER_IMP_SCHEMA_FUNCTIONS::contains) + .anyMatch(RequestStageSpecification.PER_IMP_SCHEMA_FUNCTIONS::contains) ? new PerImpMatchingRule(requestMatchingRule) : requestMatchingRule; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestStageSpecification.java similarity index 95% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestStageSpecification.java index d157a95cdfb..953dd11a74b 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestStageSpecification.java @@ -40,8 +40,7 @@ import java.util.Set; import java.util.random.RandomGenerator; -public class RequestSpecification implements - StageSpecification { +public class RequestStageSpecification implements StageSpecification { public static final Set PER_IMP_SCHEMA_FUNCTIONS = Set.of(AdUnitCodeFunction.NAME, AdUnitCodeInFunction.NAME, MediaTypeInFunction.NAME); @@ -49,9 +48,9 @@ public class RequestSpecification implements private final Map> schemaFunctions; private final Map> resultFunctions; - public RequestSpecification(ObjectMapper mapper, - BidderCatalog bidderCatalog, - RandomGenerator random) { + public RequestStageSpecification(ObjectMapper mapper, + BidderCatalog bidderCatalog, + RandomGenerator random) { schemaFunctions = new HashMap<>(); schemaFunctions.put(AdUnitCodeFunction.NAME, new AdUnitCodeFunction()); From d38b9efb48a9a7c868d945e71bca5534a60c377f Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 19:32:00 +0300 Subject: [PATCH 60/94] Fixes. --- .../schema/functions/FpdAvailableFunction.java | 8 ++++---- .../schema/functions/UserFpdAvailableFunction.java | 4 ++-- .../engine/core/rules/ConditionMatchingRule.java | 6 +++--- .../modules/rule/engine/core/util/ListUtil.java | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ListUtil.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java index 9b6ec28d121..66c19e14b36 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunction.java @@ -9,12 +9,12 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ListUtil; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.proto.openrtb.ext.request.ExtApp; import org.prebid.server.proto.openrtb.ext.request.ExtSite; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import java.util.List; import java.util.Optional; import java.util.function.Predicate; @@ -39,7 +39,7 @@ public String extract(SchemaFunctionArguments ar private static boolean isUserDataAvailable(BidRequest bidRequest) { return Optional.ofNullable(bidRequest.getUser()) .map(User::getData) - .filter(Predicate.not(List::isEmpty)) + .filter(ListUtil::isNotEmpty) .isPresent(); } @@ -55,7 +55,7 @@ private static boolean isSiteContentDataAvailable(BidRequest bidRequest) { return Optional.ofNullable(bidRequest.getSite()) .map(Site::getContent) .map(Content::getData) - .filter(Predicate.not(List::isEmpty)) + .filter(ListUtil::isNotEmpty) .isPresent(); } @@ -71,7 +71,7 @@ private static boolean isAppContentDataAvailable(BidRequest bidRequest) { return Optional.ofNullable(bidRequest.getApp()) .map(App::getContent) .map(Content::getData) - .filter(Predicate.not(List::isEmpty)) + .filter(ListUtil::isNotEmpty) .isPresent(); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java index 8872fd17674..2a4ecc83206 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunction.java @@ -6,10 +6,10 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ListUtil; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.proto.openrtb.ext.request.ExtUser; -import java.util.List; import java.util.Optional; import java.util.function.Predicate; @@ -23,7 +23,7 @@ public String extract(SchemaFunctionArguments ar .map(BidRequest::getUser); final boolean userDataAvailable = user.map(User::getData) - .filter(Predicate.not(List::isEmpty)) + .filter(ListUtil::isNotEmpty) .isPresent(); final boolean userExtDataAvailable = user.map(User::getExt) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java index bd762b1815e..67dd4b84cbf 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java @@ -28,14 +28,14 @@ public class ConditionMatchingRule implements Rule { public ConditionMatchingRule(Schema schema, RuleTree> ruleTree, - String modelVersion, - String analyticsKey) { + String analyticsKey, + String modelVersion) { this.schema = Objects.requireNonNull(schema); this.ruleTree = Objects.requireNonNull(ruleTree); - this.modelVersion = StringUtils.defaultString(modelVersion); this.analyticsKey = StringUtils.defaultString(analyticsKey); + this.modelVersion = StringUtils.defaultString(modelVersion); } @Override diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ListUtil.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ListUtil.java new file mode 100644 index 00000000000..f1dd471b045 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ListUtil.java @@ -0,0 +1,14 @@ +package org.prebid.server.hooks.modules.rule.engine.core.util; + +import java.util.Collection; +import java.util.Objects; + +public class ListUtil { + + private ListUtil() { + } + + public static boolean isNotEmpty(Collection collection) { + return collection != null && !collection.isEmpty() && collection.stream().anyMatch(Objects::nonNull); + } +} From 62d7ddbfb8fa2f9d3fc0d13d93a7e12cae9fd1bf Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 22:59:38 +0300 Subject: [PATCH 61/94] Added a bunch of unit tests for schema functions, some fixes. --- .../functions/EidAvailableFunction.java | 3 +- .../rule/engine/core/rules/NoOpRule.java | 7 +- .../functions/EidAvailableFunctionTest.java | 85 ++++++++ .../schema/functions/EidInFunctionTest.java | 114 +++++++++++ .../functions/FpdAvailableFunctionTest.java | 160 +++++++++++++++ .../GppSidAvailableFunctionTest.java | 97 +++++++++ .../functions/MediaTypeInFunctionTest.java | 186 ++++++++++++++++++ 7 files changed, 645 insertions(+), 7 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java index 3fa536bf966..92116d75693 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunction.java @@ -6,6 +6,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ListUtil; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import java.util.List; @@ -21,7 +22,7 @@ public String extract(SchemaFunctionArguments ar final boolean available = Optional.of(arguments.getOperand()) .map(BidRequest::getUser) .map(User::getEids) - .filter(Predicate.not(List::isEmpty)) + .filter(ListUtil::isNotEmpty) .isPresent(); return Boolean.toString(available); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java index e09170d544c..24814c8e188 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -1,17 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.Value; -import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.model.UpdateResult; - -import java.util.Collections; @Value(staticConstructor = "create") public class NoOpRule implements Rule { @Override public RuleResult process(T value, C context) { - return RuleResult.of( - UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList()), Collections.emptyList()); + return RuleResult.unaltered(value); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java new file mode 100644 index 00000000000..c8fb959133a --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java @@ -0,0 +1,85 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.User; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EidAvailableFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final EidAvailableFunction target = new EidAvailableFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnTrueWhenEidsArePresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().eids(Collections.singletonList(Eid.builder().build())).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenEidsAreAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + @Test + public void extractShouldReturnFalseWhenEidsListContainsOnlyNulls() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().eids(Collections.singletonList(null)).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java new file mode 100644 index 00000000000..42a0e75e59b --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java @@ -0,0 +1,114 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Eid; +import com.iab.openrtb.request.User; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EidInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final EidInFunction target = new EidInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sources' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sources' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("sources", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sources' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode sourcesNode = mapper.createArrayNode(); + sourcesNode.add(TextNode.valueOf("test")); + sourcesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("sources", sourcesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sources' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenAnyOfUserEidSourcesPresentInConfiguredSources() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().eids(Collections.singletonList(Eid.builder().source("source").build())).build()) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "source"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenAllUserEidSourcesAbsentInConfiguredSources() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().eids(Collections.singletonList(Eid.builder().source("source").build())).build()) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "expectedSource"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String... sources) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithSources(sources), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + + private ObjectNode givenConfigWithSources(String... sources) { + final ArrayNode sourcesNode = mapper.createArrayNode(); + Arrays.stream(sources).map(TextNode::valueOf).forEach(sourcesNode::add); + return mapper.createObjectNode().set("sources", sourcesNode); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java new file mode 100644 index 00000000000..0c7febd95e9 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java @@ -0,0 +1,160 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Content; +import com.iab.openrtb.request.Data; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.User; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.proto.openrtb.ext.request.ExtApp; +import org.prebid.server.proto.openrtb.ext.request.ExtSite; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class FpdAvailableFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final FpdAvailableFunction target = new FpdAvailableFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnTrueWhenUserDataPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().data(Collections.singletonList(Data.builder().build())).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenUserExtDataPresent() { + // given + final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().ext(ExtUser.builder().data(extData).build()).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenSiteContentDataPresent() { + // given + final Site site = Site.builder() + .content(Content.builder().data(Collections.singletonList(Data.builder().build())).build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenSiteExtDataPresent() { + // given + final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final Site site = Site.builder() + .ext(ExtSite.of(null, extData)) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .site(site) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenAppContentDataPresent() { + // given + final App app = App.builder() + .content(Content.builder().data(Collections.singletonList(Data.builder().build())).build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .app(app) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenAppExtDataPresent() { + // given + final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final App app = App.builder() + .ext(ExtApp.of(null, extData)) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .app(app) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenNoFpdAvailable() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java new file mode 100644 index 00000000000..532dcb081a3 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java @@ -0,0 +1,97 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Regs; +import com.iab.openrtb.request.User; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class GppSidAvailableFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final GppSidAvailableFunction target = new GppSidAvailableFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnTrueWhenPositiveGppSidIsPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .regs(Regs.builder().gppSid(Collections.singletonList(1)).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenGppSidIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + @Test + public void extractShouldReturnFalseWhenGppSidListContainsOnlyNulls() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().eids(Collections.singletonList(null)).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + @Test + public void extractShouldReturnFalseWhenGppSidListContainsOnlyNonPositiveValues() { + // given + final BidRequest bidRequest = BidRequest.builder() + .regs(Regs.builder().gppSid(List.of(-1, 0)).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java new file mode 100644 index 00000000000..810bb523477 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java @@ -0,0 +1,186 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.Audio; +import com.iab.openrtb.request.Banner; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Native; +import com.iab.openrtb.request.Video; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MediaTypeInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final MediaTypeInFunction target = new MediaTypeInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("types", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of strings"); + } + + @Test + public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode typesNode = mapper.createArrayNode(); + typesNode.add(TextNode.valueOf("test")); + typesNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("types", typesNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'types' is required and has to be an array of strings"); + } + + @Test + public void extractShouldReturnTrueWhenBannerPresentOnProvidedImpAndConfiguredTypes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .banner(Banner.builder().build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(Collections.singletonList(imp)) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "impId", "banner"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenVideoPresentOnProvidedImpAndConfiguredTypes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .video(Video.builder().build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(Collections.singletonList(imp)) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "impId", "video"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenAudioPresentOnProvidedImpAndConfiguredTypes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .audio(Audio.builder().build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(Collections.singletonList(imp)) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "impId", "audio"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenNativePresentOnProvidedImpAndConfiguredTypes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .xNative(Native.builder().build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(Collections.singletonList(imp)) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "impId", "native"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenImpMediaTypeIsAbsentInConfiguredTypes() { + // given + final Imp imp = Imp.builder() + .id("impId") + .xNative(Native.builder().build()) + .build(); + + final BidRequest bidRequest = BidRequest.builder() + .imp(Collections.singletonList(imp)) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "impId", "expectedMediaType"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String impId, + String... types) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithTypes(types), + RequestRuleContext.of(AuctionContext.builder().build(), new Granularity.Imp(impId), "datacenter")); + } + + private ObjectNode givenConfigWithTypes(String... types) { + final ArrayNode typesNode = mapper.createArrayNode(); + Arrays.stream(types).map(TextNode::valueOf).forEach(typesNode::add); + return mapper.createObjectNode().set("types", typesNode); + } +} From 9be5c956e798b241efd7cdc34ad45618d558be44 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 8 Aug 2025 23:30:17 +0300 Subject: [PATCH 62/94] Finished schema functions unit tests. --- .../functions/MediaTypeInFunctionTest.java | 6 +- .../functions/PrebidKeyFunctionTest.java | 93 +++++++++++++++++++ .../functions/TcfInScopeFunctionTest.java | 67 +++++++++++++ .../UserFpdAvailableFunctionTest.java | 85 +++++++++++++++++ 4 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java index 810bb523477..b6a4bcd8e7e 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java @@ -40,7 +40,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { } @Test - public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { + public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { // when and then assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) @@ -48,7 +48,7 @@ public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { } @Test - public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { + public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { // given final ObjectNode config = mapper.createObjectNode().set("types", TextNode.valueOf("test")); @@ -59,7 +59,7 @@ public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { } @Test - public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArrayOfStrings() { + public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArrayOfStrings() { // given final ArrayNode typesNode = mapper.createArrayNode(); typesNode.add(TextNode.valueOf("test")); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java new file mode 100644 index 00000000000..2467191481c --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java @@ -0,0 +1,93 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.proto.openrtb.ext.request.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class PrebidKeyFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final PrebidKeyFunction target = new PrebidKeyFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'key' is required and has to be a string"); + } + + @Test + public void validateConfigShouldThrowErrorWhenKeyFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'key' is required and has to be a string"); + } + + @Test + public void validateConfigShouldThrowErrorWhenKeyFieldIsNotAString() { + // given + final ObjectNode config = mapper.createObjectNode().set("key", IntNode.valueOf(1)); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'key' is required and has to be a string"); + } + + @Test + public void extractShouldReturnExtPrebidKvpsValueBySpecifiedKey() { + // given + final ObjectNode extPrebidKvpsNode = mapper.createObjectNode().set("key", TextNode.valueOf("value")); + final BidRequest bidRequest = BidRequest.builder() + .ext(ExtRequest.of(ExtRequestPrebid.builder().kvps(extPrebidKvpsNode).build())) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "key"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("value"); + } + + @Test + public void extractShouldFallbackToUndefinedWhenExtPrebidKvpsValueBySpecifiedKeyIsAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, "key"); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("undefined"); + } + + private SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest, + String key) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithKey(key), + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } + + private ObjectNode givenConfigWithKey(String key) { + return mapper.createObjectNode().set("key", TextNode.valueOf(key)); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java new file mode 100644 index 00000000000..0b33bb9e1bd --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java @@ -0,0 +1,67 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Regs; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class TcfInScopeFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final TcfInScopeFunction target = new TcfInScopeFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnTrueWhenTcfInScope() { + // given + final BidRequest bidRequest = BidRequest.builder() + .regs(Regs.builder().gdpr(1).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenTcfNotInScope() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java new file mode 100644 index 00000000000..5e0ec43abb3 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java @@ -0,0 +1,85 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Data; +import com.iab.openrtb.request.User; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class UserFpdAvailableFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final UserFpdAvailableFunction target = new UserFpdAvailableFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { + // given + final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("No arguments allowed"); + } + + @Test + public void extractShouldReturnTrueWhenNonNullUserDataIsPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().data(Collections.singletonList(Data.builder().build())).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnTrueWhenUserExtDataIsPresent() { + // given + final ObjectNode extUserData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder().ext(ExtUser.builder().data(extUserData).build()).build()) + .build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenUserDataAndUserExtDataAreAbsent() { + // given + final BidRequest bidRequest = BidRequest.builder().build(); + + final SchemaFunctionArguments arguments = givenFunctionArguments(bidRequest); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private static SchemaFunctionArguments givenFunctionArguments( + BidRequest bidRequest) { + + return SchemaFunctionArguments.of( + bidRequest, + null, + RequestRuleContext.of(AuctionContext.builder().build(), Granularity.Request.instance(), "datacenter")); + } +} From 9262c062839fffba36d746be881d6abf2d01df35 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Sat, 9 Aug 2025 00:27:24 +0300 Subject: [PATCH 63/94] Added unit test for condition matching rule. --- .../core/rules/ConditionMatchingRuleTest.java | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java new file mode 100644 index 00000000000..8e16bebec61 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java @@ -0,0 +1,174 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.LookupResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import org.prebid.server.util.ListUtil; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +public class ConditionMatchingRuleTest { + + private final ObjectMapper mapper = new ObjectMapper(); + + private static final String ANALYTICS_KEY = "analyticsKey"; + private static final String MODEL_VERSION = "modelVersion"; + + private ConditionMatchingRule target; + + @Mock(strictness = LENIENT) + private Schema schema; + + @Mock(strictness = LENIENT) + private RuleTree> ruleTree; + + @Mock(strictness = LENIENT) + private SchemaFunction firstSchemaFunction; + @Mock(strictness = LENIENT) + private SchemaFunction secondSchemaFunction; + @Mock(strictness = LENIENT) + private ResultFunction firstResultFunction; + @Mock(strictness = LENIENT) + private ResultFunction secondResultFunction; + + @BeforeEach + public void setUp() { + target = new ConditionMatchingRule<>(schema, ruleTree, ANALYTICS_KEY, MODEL_VERSION); + } + + @Test + public void processShouldCorrectlyProcessData() { + // given + final Object value = new Object(); + final Object context = new Object(); + + // two schema functions + final ObjectNode firstSchemaFunctionConfig = mapper.createObjectNode(); + final ObjectNode secondSchemaFunctionConfig = mapper.createObjectNode(); + final String firstSchemaFunctionName = "firstFunction"; + final String secondSchemaFunctionName = "secondFunction"; + final String firstSchemaFunctionOutput = "firstSchemaOutput"; + final String secondSchemaFunctionOutput = "secondSchemaOutput"; + + given(schema.getFunctions()).willReturn(List.of( + SchemaFunctionHolder.of(firstSchemaFunctionName, firstSchemaFunction, firstSchemaFunctionConfig), + SchemaFunctionHolder.of(secondSchemaFunctionName, secondSchemaFunction, secondSchemaFunctionConfig))); + + given(firstSchemaFunction.extract(eq(SchemaFunctionArguments.of(value, firstSchemaFunctionConfig, context)))) + .willReturn(firstSchemaFunctionOutput); + given(secondSchemaFunction.extract(eq(SchemaFunctionArguments.of(value, secondSchemaFunctionConfig, context)))) + .willReturn(secondSchemaFunctionOutput); + + // two result functions + final String firstRuleActionName = "firstRuleAction"; + final String secondRuleActionName = "secondRuleAction"; + final ObjectNode firstResultFunctionConfig = mapper.createObjectNode(); + final ObjectNode secondResultFunctionConfig = mapper.createObjectNode(); + final List> ruleActions = List.of( + RuleAction.of(firstRuleActionName, firstResultFunction, firstResultFunctionConfig), + RuleAction.of(secondRuleActionName, secondResultFunction, secondResultFunctionConfig)); + + final RuleConfig ruleConfig = RuleConfig.of("ruleCondition", ruleActions); + + // tree that matches values based on schema functions outputs + final String firstDimensionMatch = "firstMatch"; + final String secondDimensionMatch = "secondMatch"; + given(ruleTree.lookup(eq(List.of(firstSchemaFunctionOutput, secondSchemaFunctionOutput)))) + .willReturn(LookupResult.of(ruleConfig, List.of(firstDimensionMatch, secondDimensionMatch))); + + // infrastructure arguments passed to result functions + final InfrastructureArguments infrastructureArguments = InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults( + Map.of(firstSchemaFunctionName, firstSchemaFunctionOutput, + secondSchemaFunctionName, secondSchemaFunctionOutput)) + .schemaFunctionMatches( + Map.of(firstSchemaFunctionName, firstDimensionMatch, + secondSchemaFunctionName, secondDimensionMatch)) + .ruleFired(ruleConfig.getCondition()) + .analyticsKey(ANALYTICS_KEY) + .modelVersion(MODEL_VERSION) + .build(); + + // result of first result function processing + final Object firstResultFunctionUpdatedValue = new Object(); + final Tags firstTags = TagsImpl.of( + Collections.singletonList( + ActivityImpl.of("firstActivity", "status", Collections.emptyList()))); + final List firstSeatNonBids = Collections.singletonList( + SeatNonBid.of( + "seatA", + Collections.singletonList(NonBid.of("impIdA", BidRejectionReason.NO_BID)))); + + final RuleResult firstResultFunctionOutput = RuleResult.of( + UpdateResult.updated(firstResultFunctionUpdatedValue), + firstTags, + firstSeatNonBids); + + final ResultFunctionArguments firstResultFunctionArgs = ResultFunctionArguments.of( + value, firstResultFunctionConfig, infrastructureArguments); + + given(firstResultFunction.apply(eq(firstResultFunctionArgs))).willReturn(firstResultFunctionOutput); + + // result of second result function processing + final Object secondResultFunctionUpdatedValue = new Object(); + final Tags secondTags = TagsImpl.of( + Collections.singletonList( + ActivityImpl.of("secondActivity", "status", Collections.emptyList()))); + final List secondSeatNonBids = Collections.singletonList( + SeatNonBid.of( + "seatB", + Collections.singletonList(NonBid.of("impIdB", BidRejectionReason.NO_BID)))); + + final RuleResult secondResultFunctionOutput = RuleResult.of( + UpdateResult.updated(secondResultFunctionUpdatedValue), + secondTags, + secondSeatNonBids); + + final ResultFunctionArguments secondResultFunctionArgs = ResultFunctionArguments.of( + firstResultFunctionOutput.getUpdateResult().getValue(), + secondResultFunctionConfig, + infrastructureArguments); + + given(secondResultFunction.apply(eq(secondResultFunctionArgs))).willReturn(secondResultFunctionOutput); + + // when + final RuleResult result = target.process(value, context); + + // then + assertThat(result).isEqualTo( + RuleResult.of( + secondResultFunctionOutput.getUpdateResult(), + TagsImpl.of(ListUtil.union(firstTags.activities(), secondTags.activities())), + ListUtil.union(firstSeatNonBids, secondSeatNonBids))); + } +} From 6536ad09bedccbd29ecbe236e31813199e8bbb34 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 16:59:31 +0300 Subject: [PATCH 64/94] Added tests for stage config parser. --- .../engine/core/config/StageConfigParser.java | 2 +- .../core/config/model/AccountConfig.java | 4 +- .../core/config/model/ModelGroupConfig.java | 2 + .../core/config/model/RuleSetConfig.java | 1 + .../engine/core/rules/DefaultActionRule.java | 2 + .../engine/core/rules/RandomWeightedRule.java | 12 +- .../rule/engine/core/util/WeightedEntry.java | 4 +- .../rule/engine/core/util/WeightedList.java | 2 + .../core/config/StageConfigParserTest.java | 219 ++++++++++++++++++ .../engine/core/rules/WeightedRuleTest.java | 2 +- 10 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index f7b069d7547..f533a8ba739 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -67,7 +67,7 @@ private Rule parseModelGroupConfigs(List modelGroupConfi .map(config -> WeightedEntry.of(config.getWeight(), parseModelGroupConfig(config))) .toList(); - return new RandomWeightedRule<>(randomGenerator, new WeightedList<>(weightedRules)); + return RandomWeightedRule.of(randomGenerator, new WeightedList<>(weightedRules)); } private Rule parseModelGroupConfig(ModelGroupConfig config) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java index d005d7f1d03..552da0ac346 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java @@ -6,6 +6,7 @@ import lombok.extern.jackson.Jacksonized; import java.time.Instant; +import java.util.ArrayList; import java.util.List; @Builder @@ -19,6 +20,7 @@ public class AccountConfig { @Builder.Default Instant timestamp = Instant.EPOCH; + @Builder.Default @JsonProperty("ruleSets") - List ruleSets; + List ruleSets = new ArrayList<>(); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index 4504ffcb3d5..b1fa3d0c2b9 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -1,10 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.config.model; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Value; import java.util.List; +@Builder @Value(staticConstructor = "of") public class ModelGroupConfig { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java index c845403f49c..06648313f9d 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java @@ -13,6 +13,7 @@ @Value(staticConstructor = "of") public class RuleSetConfig { + @Builder.Default boolean enabled = true; Stage stage; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index 042d155d24f..e2664c9a4d6 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -1,5 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +import lombok.EqualsAndHashCode; import org.apache.commons.collections4.ListUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; @@ -9,6 +10,7 @@ import java.util.List; import java.util.Objects; +@EqualsAndHashCode public class DefaultActionRule implements Rule { private static final String RULE_NAME = "default"; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java index 66113fd6b59..5ecdfc580f7 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java @@ -1,19 +1,15 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; +import lombok.Value; import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; -import java.util.Objects; import java.util.random.RandomGenerator; +@Value(staticConstructor = "of") public class RandomWeightedRule implements Rule { - private final RandomGenerator random; - private final WeightedList> weightedList; - - public RandomWeightedRule(RandomGenerator random, WeightedList> weightedList) { - this.random = Objects.requireNonNull(random); - this.weightedList = Objects.requireNonNull(weightedList); - } + RandomGenerator random; + WeightedList> weightedList; @Override public RuleResult process(T value, C context) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java index 0edbccce8eb..7dd53f148d4 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java @@ -1,8 +1,8 @@ package org.prebid.server.hooks.modules.rule.engine.core.util; -import lombok.Getter; +import lombok.Value; -@Getter +@Value public class WeightedEntry { int weight; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java index 4aa71a15c2d..7bc3c1cb707 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java @@ -1,10 +1,12 @@ package org.prebid.server.hooks.modules.rule.engine.core.util; +import lombok.EqualsAndHashCode; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; +@EqualsAndHashCode public class WeightedList { private final List> entries; diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java new file mode 100644 index 00000000000..d2fc2d242df --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java @@ -0,0 +1,219 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.hooks.execution.model.Stage; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.AccountRuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.ModelGroupConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.ResultFunctionConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.RuleSetConfig; +import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTreeFactory; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedEntry; +import org.prebid.server.hooks.modules.rule.engine.core.util.WeightedList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.random.RandomGenerator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class StageConfigParserTest { + + private StageConfigParser target; + + @Mock(strictness = LENIENT) + private RandomGenerator randomGenerator; + + @Mock(strictness = LENIENT) + private StageSpecification stageSpecification; + + @Mock(strictness = LENIENT) + private MatchingRuleFactory matchingRuleFactory; + + @Mock(strictness = LENIENT) + private SchemaFunction schemaFunction; + + @Mock(strictness = LENIENT) + private ResultFunction resultFunction; + + @Mock(strictness = LENIENT) + private Rule matchingRule; + + @Mock(strictness = LENIENT) + private RuleTreeFactory ruleTreeFactory; + + @BeforeEach + public void setUp() { + target = new StageConfigParser<>( + randomGenerator, Stage.processed_auction_request, stageSpecification, matchingRuleFactory); + } + + @Test + public void parseShouldReturnNoOpRuleWhenSubrulesForStageAreAbsent() { + // when and then + assertThat(target.parse(AccountConfig.builder().build())).isEqualTo(NoOpRule.create()); + } + + @Test + public void parseShouldReturnNoOpRuleWhenEnabledSubrulesForStageAreAbsent() { + // given + final AccountConfig accountConfig = AccountConfig.builder() + .ruleSets(Collections.singletonList( + RuleSetConfig.builder() + .enabled(false) + .stage(Stage.processed_auction_request) + .build())) + .build(); + + // when and then + assertThat(target.parse(accountConfig)).isEqualTo(NoOpRule.create()); + } + + @Test + public void parseShouldCombineModelGroupRulesUnderSameRuleSetIntoRandomWeightedRule() { + // given + final ModelGroupConfig firstModelGroupConfig = ModelGroupConfig.builder() + .weight(1) + .analyticsKey("analyticsKey1") + .version("version1") + .schema(List.of(SchemaFunctionConfig.of("function1", null))) + .rules(List.of(AccountRuleConfig.of(List.of("condition1"), + List.of(ResultFunctionConfig.of("function2", null))))) + .build(); + + final ModelGroupConfig secondModelGroupConfig = ModelGroupConfig.builder() + .weight(2) + .analyticsKey("analyticsKey2") + .version("version2") + .schema(List.of(SchemaFunctionConfig.of("function1", null))) + .rules(List.of(AccountRuleConfig.of(List.of("condition1"), + List.of(ResultFunctionConfig.of("function2", null))))) + .build(); + + final List modelGroupConfigs = Arrays.asList(firstModelGroupConfig, secondModelGroupConfig); + + given(stageSpecification.schemaFunctionByName("function1")).willReturn(schemaFunction); + given(stageSpecification.resultFunctionByName("function2")).willReturn(resultFunction); + given(matchingRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); + + final AccountConfig accountConfig = givenAccountConfig(modelGroupConfigs); + + // when and then + final RandomWeightedRule weightedRule = RandomWeightedRule.of( + randomGenerator, + new WeightedList<>( + List.of(WeightedEntry.of(1, matchingRule), WeightedEntry.of(2, matchingRule)))); + + assertThat(target.parse(accountConfig)).isEqualTo( + CompositeRule.of(Collections.singletonList(weightedRule))); + } + + @Test + public void parseShouldCombineMatchingRuleWithDefaultUnderSameModelGroup() { + // given + final ModelGroupConfig modelGroupConfig = ModelGroupConfig.builder() + .weight(1) + .analyticsKey("analyticsKey") + .version("version") + .schema(List.of(SchemaFunctionConfig.of("function1", null))) + .defaultAction(List.of(ResultFunctionConfig.of("function3", null))) + .rules(List.of(AccountRuleConfig.of(List.of("condition1"), + List.of(ResultFunctionConfig.of("function2", null))))) + .build(); + + given(stageSpecification.schemaFunctionByName("function1")).willReturn(schemaFunction); + given(stageSpecification.resultFunctionByName("function2")).willReturn(resultFunction); + + final ResultFunction secondResultFunction = mock(ResultFunction.class); + given(stageSpecification.resultFunctionByName("function3")).willReturn(secondResultFunction); + + given(matchingRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); + + final AccountConfig accountConfig = givenAccountConfig(modelGroupConfig); + + // when and then + final DefaultActionRule defaultRule = new DefaultActionRule<>( + Collections.singletonList(RuleAction.of("function3", secondResultFunction, null)), + "analyticsKey", + "version"); + + final AlternativeActionRule alternativeRule = AlternativeActionRule.of( + matchingRule, defaultRule); + + final RandomWeightedRule weightedRule = RandomWeightedRule.of( + randomGenerator, new WeightedList<>(List.of(WeightedEntry.of(1, alternativeRule)))); + + assertThat(target.parse(accountConfig)).isEqualTo( + CompositeRule.of(Collections.singletonList(weightedRule))); + } + + @Test + public void parseShouldBuildRuleTreeAndCreateAppropriateMatchingRule() { + // given + final ModelGroupConfig modelGroupConfig = ModelGroupConfig.builder() + .weight(1) + .analyticsKey("analyticsKey") + .version("version") + .schema(List.of(SchemaFunctionConfig.of("function1", null))) + .rules(List.of(AccountRuleConfig.of(List.of("condition1"), + List.of(ResultFunctionConfig.of("function2", null))))) + .build(); + + given(stageSpecification.schemaFunctionByName("function1")).willReturn(schemaFunction); + given(stageSpecification.resultFunctionByName("function2")).willReturn(resultFunction); + + final Schema schema = Schema.of( + Collections.singletonList(SchemaFunctionHolder.of("function1", schemaFunction, null))); + + given(matchingRuleFactory.create(eq(schema), any(), eq("analyticsKey"), eq("version"))) + .willReturn(matchingRule); + + final AccountConfig accountConfig = givenAccountConfig(modelGroupConfig); + + // when and then + final RandomWeightedRule weightedRule = RandomWeightedRule.of( + randomGenerator, new WeightedList<>(List.of(WeightedEntry.of(1, matchingRule)))); + + assertThat(target.parse(accountConfig)).isEqualTo( + CompositeRule.of(Collections.singletonList(weightedRule))); + } + + private static AccountConfig givenAccountConfig(ModelGroupConfig modelGroupConfig) { + return givenAccountConfig(Collections.singletonList(modelGroupConfig)); + } + + private static AccountConfig givenAccountConfig(List modelGroupConfigs) { + return AccountConfig.builder() + .ruleSets(Collections.singletonList( + RuleSetConfig.builder() + .stage(Stage.processed_auction_request) + .modelGroups(modelGroupConfigs) + .build())) + .build(); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java index db649f33dab..2b77d7f89ee 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java @@ -23,7 +23,7 @@ public void processShouldUtilizeRuleFromWeightedList() { final RandomGenerator randomGenerator = mock(RandomGenerator.class); given(randomGenerator.nextDouble()).willReturn(0.5); - final RandomWeightedRule rule = new RandomWeightedRule<>(randomGenerator, ruleList); + final RandomWeightedRule rule = RandomWeightedRule.of(randomGenerator, ruleList); // when final RuleResult result = rule.process(new Object(), new Object()); From cbfd49b2ececc6844674cd7e81736863dc3d8953 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 17:54:41 +0300 Subject: [PATCH 65/94] Added unit test for per imp matching rule. --- .../core/request/PerImpMatchingRuleTest.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java new file mode 100644 index 00000000000..33732514e2e --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java @@ -0,0 +1,92 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import org.prebid.server.util.ListUtil; + +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +public class PerImpMatchingRuleTest { + + private PerImpMatchingRule target; + + @Mock(strictness = LENIENT) + private ConditionMatchingRule conditionMatchingRule; + + @BeforeEach + public void setUp() { + target = new PerImpMatchingRule(conditionMatchingRule); + } + + @Test + public void processShouldRunMatchingRulePerImpAndCombineResults() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(List.of(Imp.builder().id("1").build(), Imp.builder().id("2").build())) + .build(); + + final RequestRuleContext firstImpContext = RequestRuleContext.of( + AuctionContext.builder().build(), + new Granularity.Imp("1"), + null); + + final BidRequest updatedBidRequest = bidRequest.toBuilder().id("updated").build(); + final List firstActivities = singletonList(ActivityImpl.of("activity1", "success", emptyList())); + final List firstSeatNonBids = singletonList( + SeatNonBid.of("seat1", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))); + given(conditionMatchingRule.process(bidRequest, firstImpContext)).willReturn( + RuleResult.of( + UpdateResult.updated(updatedBidRequest), + TagsImpl.of(firstActivities), + firstSeatNonBids)); + + final RequestRuleContext secondImpContext = RequestRuleContext.of( + AuctionContext.builder().build(), + new Granularity.Imp("2"), + null); + + final BidRequest resultBidRequest = bidRequest.toBuilder().id("updated2").build(); + final List secondActivities = singletonList(ActivityImpl.of("activity2", "success", emptyList())); + final List secondSeatNonBids = singletonList( + SeatNonBid.of("seat2", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); + given(conditionMatchingRule.process(updatedBidRequest, secondImpContext)).willReturn( + RuleResult.of( + UpdateResult.updated(resultBidRequest), + TagsImpl.of(secondActivities), + secondSeatNonBids)); + + final RequestRuleContext requestContext = RequestRuleContext.of( + AuctionContext.builder().build(), + Granularity.Request.instance(), + null); + + // when and then + assertThat(target.process(bidRequest, requestContext)).isEqualTo( + RuleResult.of( + UpdateResult.updated(resultBidRequest), + TagsImpl.of(ListUtil.union(firstActivities, secondActivities)), + ListUtil.union(firstSeatNonBids, secondSeatNonBids))); + } +} From 8304e23246bdfd4c4249e7f3213ad9f2b6bd9c3d Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 20:46:30 +0300 Subject: [PATCH 66/94] Added tests for GppSidInFunction. --- .../functions/GppSidInFunctionTest.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java new file mode 100644 index 00000000000..8c9470b52ca --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java @@ -0,0 +1,118 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Native; +import com.iab.openrtb.request.Regs; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class GppSidInFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final GppSidInFunction target = new GppSidInFunction(); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sids' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenSidsFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sids' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenSidsFieldIsNotAnArray() { + // given + final ObjectNode config = mapper.createObjectNode().set("sids", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sids' is required and has to be an array of integers"); + } + + @Test + public void validateConfigShouldThrowErrorWhenSidsFieldIsNotAnArrayOfStrings() { + // given + final ArrayNode sidsNode = mapper.createArrayNode(); + sidsNode.add(TextNode.valueOf("test")); + sidsNode.add(IntNode.valueOf(1)); + final ObjectNode config = mapper.createObjectNode().set("sids", sidsNode); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'sids' is required and has to be an array of integers"); + } + + @Test + public void extractShouldReturnTrueWhenAnyOfRegsGppSidIsPresentInConfiguredSids() { + // given + final BidRequest bidRequest = BidRequest.builder() + .regs(Regs.builder().gppSid(Collections.singletonList(1)).build()) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, 1); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("true"); + } + + @Test + public void extractShouldReturnFalseWhenAllOfRegsGppSidAreAbsentConfiguredSids() { + // given + final BidRequest bidRequest = BidRequest.builder() + .regs(Regs.builder().gppSid(Collections.singletonList(2)).build()) + .build(); + + final SchemaFunctionArguments arguments = + givenFunctionArguments(bidRequest, 1); + + // when and then + assertThat(target.extract(arguments)).isEqualTo("false"); + } + + private SchemaFunctionArguments givenFunctionArguments( + com.iab.openrtb.request.BidRequest bidRequest, + int... sids) { + + return SchemaFunctionArguments.of( + bidRequest, + givenConfigWithSids(sids), + RequestRuleContext.of( + AuctionContext.builder().build(), + Granularity.Request.instance(), + "datacenter")); + } + + private ObjectNode givenConfigWithSids(int... sids) { + final ArrayNode sidsNode = mapper.createArrayNode(); + Arrays.stream(sids).mapToObj(IntNode::valueOf).forEach(sidsNode::add); + return mapper.createObjectNode().set("sids", sidsNode); + } +} From 90393de2cf32751a6e770becd666598b6087cde6 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 22:37:44 +0300 Subject: [PATCH 67/94] Started writing tests for filtering result functions. --- .../functions/filter/AnalyticsMapper.java | 16 +- .../filter/FilterBiddersFunction.java | 39 +-- .../functions/filter/FilterBiddersResult.java | 23 +- .../rules/result/ResultFunctionArguments.java | 4 +- .../filter/ExcludeBiddersFunctionTest.java | 243 ++++++++++++++++++ 5 files changed, 291 insertions(+), 34 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java index 7a54bbf23b0..e97ceeceba7 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -8,7 +8,6 @@ import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.v1.analytics.Result; @@ -22,7 +21,7 @@ public class AnalyticsMapper { - private static final String ACTIVITY_NAME = "rules-filter"; + private static final String ACTIVITY_NAME = "pb-rule-engine"; private static final String SUCCESS_STATUS = "success"; private AnalyticsMapper() { @@ -47,14 +46,11 @@ public static Tags toTags(ObjectMapper mapper, return TagsImpl.of(Collections.emptyList()); } - final List impIds = - infrastructureArguments.getContext().getGranularity() instanceof Granularity.Request - ? Collections.singletonList("*") - : seatNonBids.stream() - .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) - .map(NonBid::getImpId) - .distinct() - .toList(); + final List impIds = seatNonBids.stream() + .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) + .map(NonBid::getImpId) + .distinct() + .toList(); final BidRejectionReason reason = seatNonBids.stream() .flatMap(seatNonBid -> seatNonBid.getNonBid().stream()) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index a40c28153a8..b1e677213fc 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -1,7 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; @@ -63,9 +62,17 @@ public RuleResult apply(ResultFunctionArguments updatedImps.add(imp); + + case FilterBiddersResult.Reject reject -> + seatNonBid.addAll(toSeatNonBid(imp.getId(), reject.bidders(), rejectionReason)); + + case FilterBiddersResult.Update update -> { + updatedImps.add(update.imp()); + seatNonBid.addAll(toSeatNonBid(imp.getId(), update.bidders(), rejectionReason)); + } + } } final UpdateResult updateResult = !seatNonBid.isEmpty() @@ -78,9 +85,8 @@ public RuleResult apply(ResultFunctionArguments toSeatNonBid(FilterBiddersResult filterBiddersResult, BidRejectionReason reason) { - final String impId = filterBiddersResult.getImp().getId(); - return filterBiddersResult.getBidders().stream() + private static List toSeatNonBid(String impId, Set bidders, BidRejectionReason reason) { + return bidders.stream() .map(bidder -> SeatNonBid.of(bidder, Collections.singletonList(NonBid.of(impId, reason)))) .toList(); } @@ -90,19 +96,18 @@ private FilterBiddersResult filterBidders(Imp imp, Boolean ifSyncedId, UidsCookie uidsCookie) { - final ObjectNode impExt = imp.getExt(); - final Set removedBidders = biddersToRemove(imp, bidders, ifSyncedId, uidsCookie); - - if (removedBidders.isEmpty()) { - return FilterBiddersResult.of(imp, removedBidders); + final Set biddersToRemove = biddersToRemove(imp, bidders, ifSyncedId, uidsCookie); + if (biddersToRemove.isEmpty()) { + return FilterBiddersResult.NoAction.instance(); } - final ObjectNode updatedExt = impExt.deepCopy(); + final ObjectNode updatedExt = imp.getExt().deepCopy(); final ObjectNode updatedBiddersNode = FilterUtils.bidderNode(updatedExt); - removedBidders.forEach(updatedBiddersNode::remove); + biddersToRemove.forEach(updatedBiddersNode::remove); - final Imp updatedImp = removedBidders.isEmpty() ? imp : imp.toBuilder().ext(updatedExt).build(); - return FilterBiddersResult.of(updatedImp, removedBidders); + return updatedBiddersNode.isEmpty() + ? new FilterBiddersResult.Reject(biddersToRemove) + : new FilterBiddersResult.Update(imp.toBuilder().ext(updatedExt).build(), biddersToRemove); } protected abstract Set biddersToRemove(Imp imp, @@ -128,7 +133,7 @@ public void validateConfig(ObjectNode config) { } } - private FilterBiddersFunctionConfig parseConfig(JsonNode config) { + private FilterBiddersFunctionConfig parseConfig(ObjectNode config) { try { return mapper.treeToValue(config, FilterBiddersFunctionConfig.class); } catch (JsonProcessingException e) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java index 47e946213f9..51a9ca7476e 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java @@ -1,14 +1,27 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import com.iab.openrtb.request.Imp; -import lombok.Value; +import lombok.experimental.Accessors; import java.util.Set; -@Value(staticConstructor = "of") -public class FilterBiddersResult { - Imp imp; +public sealed interface FilterBiddersResult { - Set bidders; + @Accessors(fluent = true) + record NoAction() implements FilterBiddersResult { + private static final NoAction INSTANCE = new NoAction(); + + public static NoAction instance() { + return INSTANCE; + } + } + + @Accessors(fluent = true) + record Update(Imp imp, Set bidders) implements FilterBiddersResult { + } + + @Accessors(fluent = true) + record Reject(Set bidders) implements FilterBiddersResult { + } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java index a58a980e49e..abe28017df8 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionArguments.java @@ -1,6 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules.result; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Value; @Value(staticConstructor = "of") @@ -8,7 +8,7 @@ public class ResultFunctionArguments { T operand; - JsonNode config; + ObjectNode config; InfrastructureArguments infrastructureArguments; } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java new file mode 100644 index 00000000000..17e49c923b3 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java @@ -0,0 +1,243 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.cookie.proto.Uids; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; +import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.hooks.v1.analytics.AppliedTo; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.model.UpdateResult; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +class ExcludeBiddersFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private ExcludeBiddersFunction target; + + @Mock(strictness = LENIENT) + private BidderCatalog bidderCatalog; + + @BeforeEach + void setUp() { + target = new ExcludeBiddersFunction(mapper, bidderCatalog); + } + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(null)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Configuration is required, but not provided"); + } + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { + // given + final ObjectNode config = mapper.createObjectNode().set("bidders", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class); + } + + @Test + public void validateConfigShouldThrowErrorWhenBiddersFieldIsEmpty() { + // given + final ObjectNode config = mapper.createObjectNode(); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("'bidders' field is required"); + } + + @Test + void applyShouldExcludeBiddersSpecifiedInConfigAndEmitSeatNonBidsWithATags() { + // given + final BidRequest bidRequest = givenBidRequest(givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder1")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder1")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("impId")).build(); + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder1", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + UpdateResult.updated(givenBidRequest(givenImp("impId", "bidder2"))), + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularityIsImp() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder3", "bidder4")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId2"), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder3")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId2")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder3", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + final BidRequest expectedBidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder4")); + + assertThat(result).isEqualTo( + RuleResult.of( + UpdateResult.updated(expectedBidRequest), + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + private static Tags givenATags(Activity... activities) { + return TagsImpl.of(Arrays.asList(activities)); + } + + private static AuctionContext givenAuctionContext(String... liveUidBidders) { + final UidsCookie uidsCookie = new UidsCookie( + Uids.builder().uids(new HashMap<>()).build(), new JacksonMapper(mapper)); + + Arrays.stream(liveUidBidders).forEach(bidder -> uidsCookie.updateUid(bidder, "uid")); + + return AuctionContext.builder() + .uidsCookie(uidsCookie) + .build(); + } + + private static BidRequest givenBidRequest(Imp... imps) { + return BidRequest.builder().imp(Arrays.asList(imps)).build(); + } + + private static Imp givenImp(String impId, String... bidders) { + return Imp.builder().id(impId).ext(givenImpExt(bidders)).build(); + } + + private static ObjectNode givenImpExt(String... bidders) { + final ObjectNode biddersNode = mapper.createObjectNode(); + final ObjectNode dummyBidderConfigNode = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + Arrays.stream(bidders).forEach(bidder -> biddersNode.set(bidder, dummyBidderConfigNode)); + + return mapper.createObjectNode() + .set("prebid", mapper.createObjectNode().set("bidder", biddersNode)); + } +} From 766298af4fb7896069235dbe3be93fb2039b2cfd Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 23:36:46 +0300 Subject: [PATCH 68/94] Added ability for result functions to reject payload. --- .../engine/core/config/StageConfigParser.java | 12 +-- .../core/request/PerImpMatchingRule.java | 27 ++++--- .../filter/FilterBiddersFunction.java | 17 +++-- .../result/functions/log/LogATagFunction.java | 4 +- .../rule/engine/core/rules/CompositeRule.java | 14 +++- .../core/rules/ConditionMatchingRule.java | 29 ++++--- .../engine/core/rules/DefaultActionRule.java | 23 +++--- .../rule/engine/core/rules/RuleAction.java | 6 ++ .../rule/engine/core/rules/RuleConfig.java | 4 +- .../rule/engine/core/rules/RuleResult.java | 35 +++++++-- ...eAction.java => ResultFunctionHolder.java} | 2 +- ...RuleEngineProcessedAuctionRequestHook.java | 26 ++++--- .../core/config/StageConfigParserTest.java | 4 +- .../core/request/PerImpMatchingRuleTest.java | 11 ++- .../filter/ExcludeBiddersFunctionTest.java | 75 ++++++++++++++++++- .../engine/core/rules/CompositeRuleTest.java | 9 ++- .../core/rules/ConditionMatchingRuleTest.java | 22 +++--- 17 files changed, 233 insertions(+), 87 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleAction.java rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/{RuleAction.java => ResultFunctionHolder.java} (86%) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index f533a8ba739..12835f1117c 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -17,7 +17,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.InvalidMatcherConfiguration; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; @@ -123,7 +123,7 @@ private void validateFunctionConfig(SchemaFunctionHolder holder) { private RuleConfig parseRuleConfig(AccountRuleConfig ruleConfig) { final String ruleFired = String.join("|", ruleConfig.getConditions()); - final List> actions = parseActions(ruleConfig.getResults()); + final List> actions = parseActions(ruleConfig.getResults()); return RuleConfig.of(ruleFired, actions); } @@ -139,9 +139,9 @@ private Rule parseDefaultActionRule(ModelGroupConfig config) { parseActions(defaultActionConfig), config.getAnalyticsKey(), config.getVersion()); } - private List> parseActions(List functionConfigs) { - final List> actions = functionConfigs.stream() - .map(config -> RuleAction.of( + private List> parseActions(List functionConfigs) { + final List> actions = functionConfigs.stream() + .map(config -> ResultFunctionHolder.of( config.getFunction(), specification.resultFunctionByName(config.getFunction()), config.getArgs())) @@ -152,7 +152,7 @@ private List> parseActions(List functionC return actions; } - private void validateActionConfig(RuleAction action) { + private void validateActionConfig(ResultFunctionHolder action) { try { action.getFunction().validateConfig(action.getConfig()); } catch (ConfigurationValidationException exception) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java index 2808df051b0..93d0aaae0f0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java @@ -1,6 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; @@ -17,15 +18,21 @@ public PerImpMatchingRule(ConditionMatchingRule @Override public RuleResult process(BidRequest value, RequestRuleContext context) { - return value.getImp().stream().reduce( - RuleResult.unaltered(value), - (result, imp) -> result.mergeWith( - delegate.process( - result.getUpdateResult().getValue(), - RequestRuleContext.of( - context.getAuctionContext(), - new Granularity.Imp(imp.getId()), - context.getDatacenter()))), - RuleResult::mergeWith); + RuleResult result = RuleResult.unaltered(value); + for (Imp imp : value.getImp()) { + result = result.mergeWith(delegate.process(result.getValue(), contextForImp(context, imp))); + + if (result.isReject()) + return result; + } + + return result; + } + + private RequestRuleContext contextForImp(RequestRuleContext context, Imp imp) { + return RequestRuleContext.of( + context.getAuctionContext(), + new Granularity.Imp(imp.getId()), + context.getDatacenter()); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index b1e677213fc..2fcbac966ae 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -11,13 +11,13 @@ import org.prebid.server.cookie.UidsCookie; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.springframework.util.CollectionUtils; @@ -75,14 +75,19 @@ public RuleResult apply(ResultFunctionArguments updateResult = !seatNonBid.isEmpty() - ? UpdateResult.updated(bidRequest.toBuilder().imp(updatedImps).build()) - : UpdateResult.unaltered(bidRequest); - final Tags tags = AnalyticsMapper.toTags( mapper, name(), seatNonBid, infrastructureArguments, config.getAnalyticsValue()); - return RuleResult.of(updateResult, tags, seatNonBid); + if (updatedImps.isEmpty()) { + return RuleResult.rejected(tags, seatNonBid); + } + + final RuleAction action = !seatNonBid.isEmpty() ? RuleAction.UPDATE : RuleAction.NO_ACTION; + final BidRequest result = action == RuleAction.UPDATE + ? bidRequest.toBuilder().imp(updatedImps).build() + : bidRequest; + + return RuleResult.of(result, action, tags, seatNonBid); } private static List toSeatNonBid(String impId, Set bidders, BidRejectionReason reason) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java index 504893d9b35..fe7b7543302 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunction.java @@ -4,12 +4,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.util.ValidationUtils; import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; import java.util.Collections; import java.util.Objects; @@ -33,7 +33,7 @@ public RuleResult apply(ResultFunctionArguments implements Rule { @Override public RuleResult process(T value, C context) { - return subrules.stream().reduce( - RuleResult.unaltered(value), - (result, rule) -> result.mergeWith(rule.process(result.getUpdateResult().getValue(), context)), - RuleResult::mergeWith); + RuleResult result = RuleResult.unaltered(value); + + for (Rule subrule : subrules) { + result = result.mergeWith(subrule.process(value, context)); + + if (result.isReject()) + return result; + } + + return result; } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java index 67dd4b84cbf..46326dfd204 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java @@ -4,6 +4,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; @@ -66,15 +67,15 @@ public RuleResult process(T value, C context) { .modelVersion(modelVersion) .build(); - return ruleConfig.getActions().stream().reduce( - RuleResult.unaltered(value), - (result, action) -> result.mergeWith( - action.getFunction().apply( - ResultFunctionArguments.of( - result.getUpdateResult().getValue(), - action.getConfig(), - infrastructureArguments))), - RuleResult::mergeWith); + RuleResult result = RuleResult.unaltered(value); + for (ResultFunctionHolder action : ruleConfig.getActions()) { + result = result.mergeWith(applyAction(action, result.getValue(), infrastructureArguments)); + + if (result.isReject()) + return result; + } + + return result; } private Map mergeWithSchema(Schema schema, List values) { @@ -83,4 +84,14 @@ private Map mergeWithSchema(Schema schema, List va .collect(Collectors.toMap( idx -> schema.getFunctions().get(idx).getName(), values::get, (left, right) -> left)); } + + private RuleResult applyAction(ResultFunctionHolder action, + T value, + InfrastructureArguments infrastructureArguments) { + + final ResultFunctionArguments arguments = ResultFunctionArguments.of( + value, action.getConfig(), infrastructureArguments); + + return action.getFunction().apply(arguments); + } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index e2664c9a4d6..d5d9e615570 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -4,7 +4,7 @@ import org.apache.commons.collections4.ListUtils; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import java.util.Collections; import java.util.List; @@ -15,12 +15,12 @@ public class DefaultActionRule implements Rule { private static final String RULE_NAME = "default"; - private final List> actions; + private final List> actions; private final String analyticsKey; private final String modelVersion; - public DefaultActionRule(List> actions, String analyticsKey, String modelVersion) { + public DefaultActionRule(List> actions, String analyticsKey, String modelVersion) { this.actions = ListUtils.emptyIfNull(actions); this.analyticsKey = Objects.requireNonNull(analyticsKey); @@ -29,14 +29,19 @@ public DefaultActionRule(List> actions, String analyticsKey, St @Override public RuleResult process(T value, C context) { - return actions.stream().reduce( - RuleResult.unaltered(value), - (result, action) -> - result.mergeWith(applyAction(action, result.getUpdateResult().getValue(), context)), - RuleResult::mergeWith); + RuleResult result = RuleResult.unaltered(value); + + for (ResultFunctionHolder action : actions) { + result = result.mergeWith(applyAction(action, result.getValue(), context)); + + if (result.isReject()) + return result; + } + + return result; } - private RuleResult applyAction(RuleAction action, T value, C context) { + private RuleResult applyAction(ResultFunctionHolder action, T value, C context) { final ResultFunctionArguments arguments = ResultFunctionArguments.of( value, action.getConfig(), infrastructureArguments(context)); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleAction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleAction.java new file mode 100644 index 00000000000..b123850bebb --- /dev/null +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleAction.java @@ -0,0 +1,6 @@ +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +public enum RuleAction { + + NO_ACTION, UPDATE, REJECT +} diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java index fd361409018..0c40c198e7c 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleConfig.java @@ -1,7 +1,7 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import lombok.Value; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import java.util.List; @@ -10,5 +10,5 @@ public class RuleConfig { String condition; - List> actions; + List> actions; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index 6138703caae..6ddef5ea5ad 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -3,7 +3,6 @@ import lombok.Value; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.prebid.server.util.ListUtil; @@ -13,23 +12,47 @@ @Value(staticConstructor = "of") public class RuleResult { - UpdateResult updateResult; + T value; + + RuleAction action; Tags analyticsTags; List seatNonBid; public RuleResult mergeWith(RuleResult other) { - final boolean updated = other.updateResult.isUpdated() || updateResult.isUpdated(); - final T value = other.updateResult.getValue(); + final T value = other.getValue(); + final RuleAction action = merge(this.action, other.getAction()); final Tags tags = TagsImpl.of(ListUtil.union(analyticsTags.activities(), other.analyticsTags.activities())); final List seatNonBids = ListUtil.union(seatNonBid, other.seatNonBid); - return RuleResult.of(UpdateResult.of(updated, value), tags, seatNonBids); + return RuleResult.of(value, action, tags, seatNonBids); + } + + private static RuleAction merge(RuleAction left, RuleAction right) { + if (left == RuleAction.REJECT || right == RuleAction.REJECT) { + return RuleAction.REJECT; + } + if (left == RuleAction.UPDATE || right == RuleAction.UPDATE) { + return RuleAction.UPDATE; + } + return RuleAction.NO_ACTION; + } + + public boolean isReject() { + return action == RuleAction.REJECT; + } + + public boolean isUpdate() { + return action == RuleAction.UPDATE; } public static RuleResult unaltered(T value) { return RuleResult.of( - UpdateResult.unaltered(value), TagsImpl.of(Collections.emptyList()), Collections.emptyList()); + value, RuleAction.NO_ACTION, TagsImpl.of(Collections.emptyList()), Collections.emptyList()); + } + + public static RuleResult rejected(Tags analyticsTags, List seatNonBids) { + return RuleResult.of(null, RuleAction.REJECT, analyticsTags, seatNonBids); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java similarity index 86% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java index 29516b291ed..5b7476511d4 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/RuleAction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/ResultFunctionHolder.java @@ -4,7 +4,7 @@ import lombok.Value; @Value(staticConstructor = "of") -public class RuleAction { +public class ResultFunctionHolder { String name; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index a0552f22b0c..a1668df1438 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -11,6 +11,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.InvocationAction; import org.prebid.server.hooks.v1.InvocationResult; @@ -18,7 +19,6 @@ import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; -import org.prebid.server.model.UpdateResult; import java.util.Objects; @@ -56,17 +56,17 @@ public Future> call(AuctionRequestPayloa } private static Future> succeeded(RuleResult result) { - final UpdateResult updateResult = result.getUpdateResult(); - - final InvocationResult invocationResult = + final InvocationResultImpl.InvocationResultImplBuilder resultBuilder = InvocationResultImpl.builder() .status(InvocationStatus.success) - .action(updateResult.isUpdated() ? InvocationAction.update : InvocationAction.no_action) - .payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(updateResult.getValue())) - .analyticsTags(result.getAnalyticsTags()) - .build(); + .action(toInvocationAction(result.getAction())) + .analyticsTags(result.getAnalyticsTags()); + + if (result.isUpdate()) { + resultBuilder.payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(result.getValue())); + } - return Future.succeededFuture(invocationResult); + return Future.succeededFuture(resultBuilder.build() ); } private static Future> failure(Throwable error) { @@ -78,6 +78,14 @@ private static Future> failure(Throwable .build()); } + private static InvocationAction toInvocationAction(RuleAction ruleAction) { + return switch (ruleAction) { + case NO_ACTION -> InvocationAction.no_action; + case UPDATE -> InvocationAction.update; + case REJECT -> InvocationAction.reject; + }; + } + @Override public String code() { return CODE; diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java index d2fc2d242df..fc0c5246ccb 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java @@ -21,7 +21,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.StageSpecification; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; @@ -158,7 +158,7 @@ public void parseShouldCombineMatchingRuleWithDefaultUnderSameModelGroup() { // when and then final DefaultActionRule defaultRule = new DefaultActionRule<>( - Collections.singletonList(RuleAction.of("function3", secondResultFunction, null)), + Collections.singletonList(ResultFunctionHolder.of("function3", secondResultFunction, null)), "analyticsKey", "version"); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java index 33732514e2e..42fd2f4ec29 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java @@ -12,9 +12,9 @@ import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.analytics.Activity; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.prebid.server.util.ListUtil; @@ -58,7 +58,8 @@ public void processShouldRunMatchingRulePerImpAndCombineResults() { SeatNonBid.of("seat1", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))); given(conditionMatchingRule.process(bidRequest, firstImpContext)).willReturn( RuleResult.of( - UpdateResult.updated(updatedBidRequest), + updatedBidRequest, + RuleAction.UPDATE, TagsImpl.of(firstActivities), firstSeatNonBids)); @@ -73,7 +74,8 @@ public void processShouldRunMatchingRulePerImpAndCombineResults() { SeatNonBid.of("seat2", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); given(conditionMatchingRule.process(updatedBidRequest, secondImpContext)).willReturn( RuleResult.of( - UpdateResult.updated(resultBidRequest), + resultBidRequest, + RuleAction.UPDATE, TagsImpl.of(secondActivities), secondSeatNonBids)); @@ -85,7 +87,8 @@ public void processShouldRunMatchingRulePerImpAndCombineResults() { // when and then assertThat(target.process(bidRequest, requestContext)).isEqualTo( RuleResult.of( - UpdateResult.updated(resultBidRequest), + resultBidRequest, + RuleAction.UPDATE, TagsImpl.of(ListUtil.union(firstActivities, secondActivities)), ListUtil.union(firstSeatNonBids, secondSeatNonBids))); } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java index 17e49c923b3..d8c789524cf 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java @@ -22,6 +22,7 @@ import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; @@ -30,7 +31,6 @@ import org.prebid.server.hooks.v1.analytics.AppliedTo; import org.prebid.server.hooks.v1.analytics.Tags; import org.prebid.server.json.JacksonMapper; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; @@ -138,7 +138,8 @@ void applyShouldExcludeBiddersSpecifiedInConfigAndEmitSeatNonBidsWithATags() { assertThat(result).isEqualTo( RuleResult.of( - UpdateResult.updated(givenBidRequest(givenImp("impId", "bidder2"))), + givenBidRequest(givenImp("impId", "bidder2")), + RuleAction.UPDATE, givenATags(expectedActivity), Collections.singletonList(expectedSeatNonBid))); } @@ -204,7 +205,75 @@ void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularit assertThat(result).isEqualTo( RuleResult.of( - UpdateResult.updated(expectedBidRequest), + expectedBidRequest, + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldDiscardImpIfAfterUpdateThereAreNoBidders() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder3", "bidder4")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId2"), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder3")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId2")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder3", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + final BidRequest expectedBidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder4")); + + assertThat(result).isEqualTo( + RuleResult.of( + expectedBidRequest, + RuleAction.UPDATE, givenATags(expectedActivity), Collections.singletonList(expectedSeatNonBid))); } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java index c350a5b56a6..e4cd9791026 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java @@ -5,7 +5,6 @@ import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; @@ -30,13 +29,15 @@ public void processShouldAccumulateResultFromAllSubrules() { // given final Rule firstRule = (Rule) mock(Rule.class); given(firstRule.process(any(), any())).willAnswer(invocationOnMock -> RuleResult.of( - UpdateResult.updated(invocationOnMock.getArgument(0)), + invocationOnMock.getArgument(0), + RuleAction.UPDATE, TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))), singletonList(SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))))); final Rule secondRule = (Rule) mock(Rule.class); given(secondRule.process(any(), any())).willAnswer(invocationOnMock -> RuleResult.of( - UpdateResult.updated(invocationOnMock.getArgument(0)), + invocationOnMock.getArgument(0), + RuleAction.UPDATE, TagsImpl.of(singletonList(ActivityImpl.of("secondActivity", "success", emptyList()))), singletonList(SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))))); @@ -54,7 +55,7 @@ public void processShouldAccumulateResultFromAllSubrules() { SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID))), SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); - assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags, expectedNonBids)); + assertThat(result).isEqualTo(RuleResult.of(VALUE, RuleAction.UPDATE, expectedTags, expectedNonBids)); verify(firstRule).process(eq(VALUE), any()); verify(secondRule).process(eq(VALUE), any()); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java index 8e16bebec61..a1ee31c8fca 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java @@ -13,7 +13,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; @@ -21,7 +21,6 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.LookupResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; import org.prebid.server.hooks.v1.analytics.Tags; -import org.prebid.server.model.UpdateResult; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.prebid.server.util.ListUtil; @@ -93,11 +92,11 @@ public void processShouldCorrectlyProcessData() { final String secondRuleActionName = "secondRuleAction"; final ObjectNode firstResultFunctionConfig = mapper.createObjectNode(); final ObjectNode secondResultFunctionConfig = mapper.createObjectNode(); - final List> ruleActions = List.of( - RuleAction.of(firstRuleActionName, firstResultFunction, firstResultFunctionConfig), - RuleAction.of(secondRuleActionName, secondResultFunction, secondResultFunctionConfig)); + final List> resultFunctionHolders = List.of( + ResultFunctionHolder.of(firstRuleActionName, firstResultFunction, firstResultFunctionConfig), + ResultFunctionHolder.of(secondRuleActionName, secondResultFunction, secondResultFunctionConfig)); - final RuleConfig ruleConfig = RuleConfig.of("ruleCondition", ruleActions); + final RuleConfig ruleConfig = RuleConfig.of("ruleCondition", resultFunctionHolders); // tree that matches values based on schema functions outputs final String firstDimensionMatch = "firstMatch"; @@ -130,7 +129,8 @@ public void processShouldCorrectlyProcessData() { Collections.singletonList(NonBid.of("impIdA", BidRejectionReason.NO_BID)))); final RuleResult firstResultFunctionOutput = RuleResult.of( - UpdateResult.updated(firstResultFunctionUpdatedValue), + firstResultFunctionUpdatedValue, + RuleAction.UPDATE, firstTags, firstSeatNonBids); @@ -150,12 +150,13 @@ public void processShouldCorrectlyProcessData() { Collections.singletonList(NonBid.of("impIdB", BidRejectionReason.NO_BID)))); final RuleResult secondResultFunctionOutput = RuleResult.of( - UpdateResult.updated(secondResultFunctionUpdatedValue), + secondResultFunctionUpdatedValue, + RuleAction.UPDATE, secondTags, secondSeatNonBids); final ResultFunctionArguments secondResultFunctionArgs = ResultFunctionArguments.of( - firstResultFunctionOutput.getUpdateResult().getValue(), + firstResultFunctionOutput.getValue(), secondResultFunctionConfig, infrastructureArguments); @@ -167,7 +168,8 @@ public void processShouldCorrectlyProcessData() { // then assertThat(result).isEqualTo( RuleResult.of( - secondResultFunctionOutput.getUpdateResult(), + secondResultFunctionOutput.getValue(), + RuleAction.UPDATE, TagsImpl.of(ListUtil.union(firstTags.activities(), secondTags.activities())), ListUtil.union(firstSeatNonBids, secondSeatNonBids))); } From 8084bb94f4af207b976516ce2e5dfe13c5255b28 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 19 Aug 2025 23:48:36 +0300 Subject: [PATCH 69/94] Finished tests for ExcludeBiddersFunction. --- .../filter/ExcludeBiddersFunctionTest.java | 160 ++++++++++++++---- 1 file changed, 127 insertions(+), 33 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java index d8c789524cf..3c5ffc6fa80 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java @@ -37,6 +37,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -95,14 +97,7 @@ void applyShouldExcludeBiddersSpecifiedInConfigAndEmitSeatNonBidsWithATags() { givenAuctionContext(), Granularity.Request.instance(), null); final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() - .context(context) - .schemaFunctionResults(Collections.emptyMap()) - .schemaFunctionMatches(Collections.emptyMap()) - .ruleFired("ruleFired") - .analyticsKey("analyticsKey") - .modelVersion("modelVersion") - .build(); + givenInfrastructureArguments(context); final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() .bidders(Collections.singleton("bidder1")) @@ -155,14 +150,7 @@ void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularit givenAuctionContext(), new Granularity.Imp("impId2"), null); final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() - .context(context) - .schemaFunctionResults(Collections.emptyMap()) - .schemaFunctionMatches(Collections.emptyMap()) - .ruleFired("ruleFired") - .analyticsKey("analyticsKey") - .modelVersion("modelVersion") - .build(); + givenInfrastructureArguments(context); final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() .bidders(Collections.singleton("bidder3")) @@ -212,7 +200,7 @@ void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularit } @Test - void applyShouldDiscardImpIfAfterUpdateThereAreNoBidders() { + void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { // given final BidRequest bidRequest = givenBidRequest( givenImp("impId", "bidder1", "bidder2"), @@ -222,17 +210,10 @@ void applyShouldDiscardImpIfAfterUpdateThereAreNoBidders() { givenAuctionContext(), new Granularity.Imp("impId2"), null); final InfrastructureArguments infrastructureArguments = - InfrastructureArguments.builder() - .context(context) - .schemaFunctionResults(Collections.emptyMap()) - .schemaFunctionMatches(Collections.emptyMap()) - .ruleFired("ruleFired") - .analyticsKey("analyticsKey") - .modelVersion("modelVersion") - .build(); + givenInfrastructureArguments(context); final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() - .bidders(Collections.singleton("bidder3")) + .bidders(Set.of("bidder3", "bidder4")) .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) .analyticsValue("analyticsValue") .build(); @@ -250,7 +231,7 @@ void applyShouldDiscardImpIfAfterUpdateThereAreNoBidders() { expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3").add("bidder4")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -262,20 +243,133 @@ void applyShouldDiscardImpIfAfterUpdateThereAreNoBidders() { "success", Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); - final SeatNonBid expectedSeatNonBid = SeatNonBid.of( - "bidder3", - Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder3", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))), + SeatNonBid.of( + "bidder4", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); final BidRequest expectedBidRequest = givenBidRequest( - givenImp("impId", "bidder1", "bidder2"), - givenImp("impId2", "bidder4")); + givenImp("impId", "bidder1", "bidder2")); assertThat(result).isEqualTo( RuleResult.of( expectedBidRequest, RuleAction.UPDATE, givenATags(expectedActivity), - Collections.singletonList(expectedSeatNonBid))); + expectedSeatNonBid)); + } + + @Test + void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId"), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Set.of("bidder")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); + + assertThat(result).isEqualTo(RuleResult.rejected(givenATags(expectedActivity), expectedSeatNonBid)); + } + + @Test + void applyShouldNotGenerateATagWhenNoAnalyticsKeySpecified() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId"), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .modelVersion("modelVersion") + .build(); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Set.of("bidder1")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder1", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder2")), + RuleAction.UPDATE, + givenATags(), + expectedSeatNonBid)); + } + + private static InfrastructureArguments givenInfrastructureArguments( + RequestRuleContext context) { + + return InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); } private static Tags givenATags(Activity... activities) { From e446f367c5baa3372e3435481f0e7c0dc4f4c2c1 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 00:33:30 +0300 Subject: [PATCH 70/94] Finished tests for filering functions. --- .../PbRuleEngineModuleConfiguration.java | 4 +- .../engine/core/config/StageConfigParser.java | 10 +- ...ngRule.java => PerImpConditionalRule.java} | 8 +- ...ava => RequestConditionalRuleFactory.java} | 10 +- .../filter/ExcludeBiddersFunction.java | 2 +- .../filter/FilterBiddersFunction.java | 4 +- .../filter/IncludeBiddersFunction.java | 7 +- ...MatchingRule.java => ConditionalRule.java} | 10 +- ...ctory.java => ConditionalRuleFactory.java} | 2 +- .../core/config/StageConfigParserTest.java | 12 +- ...st.java => PerImpConditionalRuleTest.java} | 14 +- .../filter/ExcludeBiddersFunctionTest.java | 129 ++++- .../filter/IncludeBiddersFunctionTest.java | 528 ++++++++++++++++++ ...RuleTest.java => ConditionalRuleTest.java} | 6 +- 14 files changed, 697 insertions(+), 49 deletions(-) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{PerImpMatchingRule.java => PerImpConditionalRule.java} (80%) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/{RequestMatchingRuleFactory.java => RequestConditionalRuleFactory.java} (74%) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{ConditionMatchingRule.java => ConditionalRule.java} (93%) rename extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{MatchingRuleFactory.java => ConditionalRuleFactory.java} (89%) rename extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/{PerImpMatchingRuleTest.java => PerImpConditionalRuleTest.java} (89%) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java rename extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/{ConditionMatchingRuleTest.java => ConditionalRuleTest.java} (97%) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java index fe99de6a98f..ae9a0df11f0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java @@ -8,7 +8,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.AccountConfigParser; import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.config.StageConfigParser; -import org.prebid.server.hooks.modules.rule.engine.core.request.RequestMatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestConditionalRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; import org.prebid.server.hooks.modules.rule.engine.core.request.RequestStageSpecification; import org.prebid.server.hooks.modules.rule.engine.v1.PbRuleEngineModule; @@ -42,7 +42,7 @@ StageConfigParser processedAuctionRequestStagePa randomGenerator, Stage.processed_auction_request, new RequestStageSpecification(ObjectMapperProvider.mapper(), bidderCatalog, randomGenerator), - new RequestMatchingRuleFactory()); + new RequestConditionalRuleFactory()); } @Bean diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 12835f1117c..ec592e76057 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -10,7 +10,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; @@ -36,17 +36,17 @@ public class StageConfigParser { private final RandomGenerator randomGenerator; private final StageSpecification specification; private final Stage stage; - private final MatchingRuleFactory matchingRuleFactory; + private final ConditionalRuleFactory conditionalRuleFactory; public StageConfigParser(RandomGenerator randomGenerator, Stage stage, StageSpecification specification, - MatchingRuleFactory matchingRuleFactory) { + ConditionalRuleFactory conditionalRuleFactory) { this.randomGenerator = Objects.requireNonNull(randomGenerator); this.stage = Objects.requireNonNull(stage); this.specification = Objects.requireNonNull(specification); - this.matchingRuleFactory = Objects.requireNonNull(matchingRuleFactory); + this.conditionalRuleFactory = Objects.requireNonNull(conditionalRuleFactory); } public Rule parse(AccountConfig config) { @@ -96,7 +96,7 @@ private Rule parseMatchingRule(ModelGroupConfig config) { throw new InvalidMatcherConfiguration("Schema functions count and rules matchers count mismatch"); } - return matchingRuleFactory.create(schema, ruleTree, config.getAnalyticsKey(), config.getVersion()); + return conditionalRuleFactory.create(schema, ruleTree, config.getAnalyticsKey(), config.getVersion()); } private Schema parseSchema(List schema) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java similarity index 80% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java index 93d0aaae0f0..57332d677d7 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java @@ -2,17 +2,17 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.request.Imp; -import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import java.util.Objects; -public class PerImpMatchingRule implements Rule { +public class PerImpConditionalRule implements Rule { - private final ConditionMatchingRule delegate; + private final ConditionalRule delegate; - public PerImpMatchingRule(ConditionMatchingRule delegate) { + public PerImpConditionalRule(ConditionalRule delegate) { this.delegate = Objects.requireNonNull(delegate); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactory.java similarity index 74% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactory.java index 25ccecec765..7958d1e97b9 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestMatchingRuleFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactory.java @@ -1,15 +1,15 @@ package org.prebid.server.hooks.modules.rule.engine.core.request; import com.iab.openrtb.request.BidRequest; -import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -public class RequestMatchingRuleFactory implements MatchingRuleFactory { +public class RequestConditionalRuleFactory implements ConditionalRuleFactory { @Override public Rule create( @@ -18,14 +18,14 @@ public Rule create( String analyticsKey, String modelVersion) { - final ConditionMatchingRule requestMatchingRule = new ConditionMatchingRule<>( + final ConditionalRule requestMatchingRule = new ConditionalRule<>( schema, ruleTree, analyticsKey, modelVersion); return schema.getFunctions().stream() .map(SchemaFunctionHolder::getName) .anyMatch(RequestStageSpecification.PER_IMP_SCHEMA_FUNCTIONS::contains) - ? new PerImpMatchingRule(requestMatchingRule) + ? new PerImpConditionalRule(requestMatchingRule) : requestMatchingRule; } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 52d2a0c479a..b954abd664a 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -19,7 +19,7 @@ public ExcludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCat } @Override - protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSyncedId, UidsCookie uidsCookie) { + protected Set biddersToRemove(Imp imp, Boolean ifSyncedId, Set bidders, UidsCookie uidsCookie) { final ObjectNode biddersNode = FilterUtils.bidderNode(imp.getExt()); return StreamUtil.asStream(biddersNode.fieldNames()) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 2fcbac966ae..2f74db2eae2 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -101,7 +101,7 @@ private FilterBiddersResult filterBidders(Imp imp, Boolean ifSyncedId, UidsCookie uidsCookie) { - final Set biddersToRemove = biddersToRemove(imp, bidders, ifSyncedId, uidsCookie); + final Set biddersToRemove = biddersToRemove(imp, ifSyncedId, bidders, uidsCookie); if (biddersToRemove.isEmpty()) { return FilterBiddersResult.NoAction.instance(); } @@ -116,8 +116,8 @@ private FilterBiddersResult filterBidders(Imp imp, } protected abstract Set biddersToRemove(Imp imp, - Set bidders, Boolean ifSyncedId, + Set bidders, UidsCookie uidsCookie); protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java index 38a08e2ecb1..08ea8f2b223 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -19,13 +19,12 @@ public IncludeBiddersFunction(ObjectMapper objectMapper, BidderCatalog bidderCat } @Override - protected Set biddersToRemove(Imp imp, Set bidders, Boolean ifSyncedId, UidsCookie uidsCookie) { + protected Set biddersToRemove(Imp imp, Boolean ifSyncedId, Set bidders, UidsCookie uidsCookie) { final ObjectNode biddersNode = FilterUtils.bidderNode(imp.getExt()); return StreamUtil.asStream(biddersNode.fieldNames()) - .filter(bidder -> !FilterUtils.containsIgnoreCase(bidders.stream(), bidder)) - .filter(bidder -> - ifSyncedId == null || ifSyncedId == isBidderIdSynced(bidder.toLowerCase(), uidsCookie)) + .filter(bidder -> !FilterUtils.containsIgnoreCase(bidders.stream(), bidder) + || (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder.toLowerCase(), uidsCookie))) .collect(Collectors.toSet()); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java similarity index 93% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java index 46326dfd204..fe43742765e 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java @@ -19,7 +19,7 @@ import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; -public class ConditionMatchingRule implements Rule { +public class ConditionalRule implements Rule { private final Schema schema; private final RuleTree> ruleTree; @@ -27,10 +27,10 @@ public class ConditionMatchingRule implements Rule { private final String modelVersion; private final String analyticsKey; - public ConditionMatchingRule(Schema schema, - RuleTree> ruleTree, - String analyticsKey, - String modelVersion) { + public ConditionalRule(Schema schema, + RuleTree> ruleTree, + String analyticsKey, + String modelVersion) { this.schema = Objects.requireNonNull(schema); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleFactory.java similarity index 89% rename from extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java rename to extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleFactory.java index cb4fcde7aa0..918b544e086 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/MatchingRuleFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleFactory.java @@ -3,7 +3,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; -public interface MatchingRuleFactory { +public interface ConditionalRuleFactory { Rule create(Schema schema, RuleTree> ruleTree, diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java index fc0c5246ccb..88127ec9d15 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java @@ -15,7 +15,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.MatchingRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRuleFactory; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; @@ -53,7 +53,7 @@ public class StageConfigParserTest { private StageSpecification stageSpecification; @Mock(strictness = LENIENT) - private MatchingRuleFactory matchingRuleFactory; + private ConditionalRuleFactory conditionalRuleFactory; @Mock(strictness = LENIENT) private SchemaFunction schemaFunction; @@ -70,7 +70,7 @@ public class StageConfigParserTest { @BeforeEach public void setUp() { target = new StageConfigParser<>( - randomGenerator, Stage.processed_auction_request, stageSpecification, matchingRuleFactory); + randomGenerator, Stage.processed_auction_request, stageSpecification, conditionalRuleFactory); } @Test @@ -119,7 +119,7 @@ public void parseShouldCombineModelGroupRulesUnderSameRuleSetIntoRandomWeightedR given(stageSpecification.schemaFunctionByName("function1")).willReturn(schemaFunction); given(stageSpecification.resultFunctionByName("function2")).willReturn(resultFunction); - given(matchingRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); + given(conditionalRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); final AccountConfig accountConfig = givenAccountConfig(modelGroupConfigs); @@ -152,7 +152,7 @@ public void parseShouldCombineMatchingRuleWithDefaultUnderSameModelGroup() { final ResultFunction secondResultFunction = mock(ResultFunction.class); given(stageSpecification.resultFunctionByName("function3")).willReturn(secondResultFunction); - given(matchingRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); + given(conditionalRuleFactory.create(any(), any(), any(), any())).willReturn(matchingRule); final AccountConfig accountConfig = givenAccountConfig(modelGroupConfig); @@ -190,7 +190,7 @@ public void parseShouldBuildRuleTreeAndCreateAppropriateMatchingRule() { final Schema schema = Schema.of( Collections.singletonList(SchemaFunctionHolder.of("function1", schemaFunction, null))); - given(matchingRuleFactory.create(eq(schema), any(), eq("analyticsKey"), eq("version"))) + given(conditionalRuleFactory.create(eq(schema), any(), eq("analyticsKey"), eq("version"))) .willReturn(matchingRule); final AccountConfig accountConfig = givenAccountConfig(modelGroupConfig); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRuleTest.java similarity index 89% rename from extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRuleTest.java index 42fd2f4ec29..0d50d9c2a16 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpMatchingRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRuleTest.java @@ -11,7 +11,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionMatchingRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.v1.analytics.Activity; @@ -28,16 +28,16 @@ import static org.mockito.Mock.Strictness.LENIENT; @ExtendWith(MockitoExtension.class) -public class PerImpMatchingRuleTest { +public class PerImpConditionalRuleTest { - private PerImpMatchingRule target; + private PerImpConditionalRule target; @Mock(strictness = LENIENT) - private ConditionMatchingRule conditionMatchingRule; + private ConditionalRule conditionalRule; @BeforeEach public void setUp() { - target = new PerImpMatchingRule(conditionMatchingRule); + target = new PerImpConditionalRule(conditionalRule); } @Test @@ -56,7 +56,7 @@ public void processShouldRunMatchingRulePerImpAndCombineResults() { final List firstActivities = singletonList(ActivityImpl.of("activity1", "success", emptyList())); final List firstSeatNonBids = singletonList( SeatNonBid.of("seat1", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))); - given(conditionMatchingRule.process(bidRequest, firstImpContext)).willReturn( + given(conditionalRule.process(bidRequest, firstImpContext)).willReturn( RuleResult.of( updatedBidRequest, RuleAction.UPDATE, @@ -72,7 +72,7 @@ public void processShouldRunMatchingRulePerImpAndCombineResults() { final List secondActivities = singletonList(ActivityImpl.of("activity2", "success", emptyList())); final List secondSeatNonBids = singletonList( SeatNonBid.of("seat2", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); - given(conditionMatchingRule.process(updatedBidRequest, secondImpContext)).willReturn( + given(conditionalRule.process(updatedBidRequest, secondImpContext)).willReturn( RuleResult.of( resultBidRequest, RuleAction.UPDATE, diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java index 3c5ffc6fa80..bdee427ed3c 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java @@ -15,6 +15,7 @@ import org.prebid.server.auction.model.BidRejectionReason; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.cookie.model.UidWithExpiry; import org.prebid.server.cookie.proto.Uids; import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; @@ -36,12 +37,16 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; @ExtendWith(MockitoExtension.class) @@ -199,6 +204,118 @@ void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularit Collections.singletonList(expectedSeatNonBid))); } + @Test + void applyShouldExcludeBiddersWithoutLiveUidSpecifiedInConfigWhenIfSyncedIdSetToFalse() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext("bidder1"), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder2")) + .ifSyncedId(false) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder2", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder1")), + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldExcludeBiddersWitLiveUidSpecifiedInConfigWhenIfSyncedIdSetToTrue() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext("bidder2"), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder2")) + .ifSyncedId(true) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder2", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder1")), + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + @Test void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { // given @@ -376,11 +493,15 @@ private static Tags givenATags(Activity... activities) { return TagsImpl.of(Arrays.asList(activities)); } - private static AuctionContext givenAuctionContext(String... liveUidBidders) { + private AuctionContext givenAuctionContext(String... liveUidBidders) { + final Map uids = Arrays.stream(liveUidBidders) + .collect(Collectors.toMap(Function.identity(), ignored -> UidWithExpiry.live("uid"))); + final UidsCookie uidsCookie = new UidsCookie( - Uids.builder().uids(new HashMap<>()).build(), new JacksonMapper(mapper)); + Uids.builder().uids(uids).build(), new JacksonMapper(mapper)); - Arrays.stream(liveUidBidders).forEach(bidder -> uidsCookie.updateUid(bidder, "uid")); + Arrays.stream(liveUidBidders).forEach( + bidder -> given(bidderCatalog.cookieFamilyName(bidder)).willReturn(Optional.of(bidder))); return AuctionContext.builder() .uidsCookie(uidsCookie) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java new file mode 100644 index 00000000000..57507717260 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java @@ -0,0 +1,528 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.bidder.BidderCatalog; +import org.prebid.server.cookie.UidsCookie; +import org.prebid.server.cookie.model.UidWithExpiry; +import org.prebid.server.cookie.proto.Uids; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; +import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.hooks.v1.analytics.AppliedTo; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +class IncludeBiddersFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private IncludeBiddersFunction target; + + @Mock(strictness = LENIENT) + private BidderCatalog bidderCatalog; + + @BeforeEach + void setUp() { + target = new IncludeBiddersFunction(mapper, bidderCatalog); + } + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(null)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Configuration is required, but not provided"); + } + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { + // given + final ObjectNode config = mapper.createObjectNode().set("bidders", TextNode.valueOf("test")); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class); + } + + @Test + public void validateConfigShouldThrowErrorWhenBiddersFieldIsEmpty() { + // given + final ObjectNode config = mapper.createObjectNode(); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("'bidders' field is required"); + } + + @Test + void applyShouldExcludeBiddersNotSpecifiedInConfigAndEmitSeatNonBidsWithATags() { + // given + final BidRequest bidRequest = givenBidRequest(givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder1")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("impId")).build(); + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder2", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder1")), + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldExcludeBiddersNotSpecifiedInConfigOnlyForSpecifiedImpWhenGranularityIsImp() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder3", "bidder4")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId2"), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder3")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder4")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId2")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder4", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + final BidRequest expectedBidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder3")); + + assertThat(result).isEqualTo( + RuleResult.of( + expectedBidRequest, + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + + @Test + void applyShouldExcludeBiddersWithLiveUidOrNotSpecifiedInConfigWhenIfSyncedIdSetToFalse() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder2")) + .ifSyncedId(false) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder1")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder1", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder2")), + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldExcludeBiddersWithoutLiveUidOrNotSpecifiedInConfigWhenIfSyncedIdSetToTrue() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext("bidder1"), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Collections.singleton("bidder1")) + .ifSyncedId(true) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + final SeatNonBid expectedSeatNonBid = SeatNonBid.of( + "bidder2", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder1")), + RuleAction.UPDATE, + givenATags(expectedActivity), + Collections.singletonList(expectedSeatNonBid))); + } + + @Test + void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2"), + givenImp("impId2", "bidder3", "bidder4")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId2"), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Set.of("bidder1", "bidder2")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3").add("bidder4")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId2")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder3", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL))), + SeatNonBid.of( + "bidder4", + Collections.singletonList(NonBid.of("impId2", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); + + final BidRequest expectedBidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + assertThat(result).isEqualTo( + RuleResult.of( + expectedBidRequest, + RuleAction.UPDATE, + givenATags(expectedActivity), + expectedSeatNonBid)); + } + + @Test + void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId"), null); + + final InfrastructureArguments infrastructureArguments = + givenInfrastructureArguments(context); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Set.of("bidder1")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final ObjectNode expectedResultValue = mapper.createObjectNode(); + expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); + expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder")); + expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder() + .impIds(Collections.singletonList("impId")) + .build(); + + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", + "success", + Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); + + + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); + + assertThat(result).isEqualTo(RuleResult.rejected(givenATags(expectedActivity), expectedSeatNonBid)); + } + + @Test + void applyShouldNotGenerateATagWhenNoAnalyticsKeySpecified() { + // given + final BidRequest bidRequest = givenBidRequest( + givenImp("impId", "bidder1", "bidder2")); + + final RequestRuleContext context = RequestRuleContext.of( + givenAuctionContext(), new Granularity.Imp("impId"), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .modelVersion("modelVersion") + .build(); + + final FilterBiddersFunctionConfig config = FilterBiddersFunctionConfig.builder() + .bidders(Set.of("bidder1")) + .seatNonBid(BidRejectionReason.REQUEST_BLOCKED_GENERAL) + .analyticsValue("analyticsValue") + .build(); + + final ResultFunctionArguments arguments = + ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + + // when + final RuleResult result = target.apply(arguments); + + // then + final List expectedSeatNonBid = List.of( + SeatNonBid.of( + "bidder2", + Collections.singletonList(NonBid.of("impId", BidRejectionReason.REQUEST_BLOCKED_GENERAL)))); + + assertThat(result).isEqualTo( + RuleResult.of( + givenBidRequest(givenImp("impId", "bidder1")), + RuleAction.UPDATE, + givenATags(), + expectedSeatNonBid)); + } + + private static InfrastructureArguments givenInfrastructureArguments( + RequestRuleContext context) { + + return InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + } + + private static Tags givenATags(Activity... activities) { + return TagsImpl.of(Arrays.asList(activities)); + } + + private AuctionContext givenAuctionContext(String... liveUidBidders) { + final Map uids = Arrays.stream(liveUidBidders) + .collect(Collectors.toMap(Function.identity(), ignored -> UidWithExpiry.live("uid"))); + + final UidsCookie uidsCookie = new UidsCookie( + Uids.builder().uids(uids).build(), new JacksonMapper(mapper)); + + Arrays.stream(liveUidBidders).forEach( + bidder -> given(bidderCatalog.cookieFamilyName(bidder)).willReturn(Optional.of(bidder))); + + return AuctionContext.builder() + .uidsCookie(uidsCookie) + .build(); + } + + private static BidRequest givenBidRequest(Imp... imps) { + return BidRequest.builder().imp(Arrays.asList(imps)).build(); + } + + private static Imp givenImp(String impId, String... bidders) { + return Imp.builder().id(impId).ext(givenImpExt(bidders)).build(); + } + + private static ObjectNode givenImpExt(String... bidders) { + final ObjectNode biddersNode = mapper.createObjectNode(); + final ObjectNode dummyBidderConfigNode = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + Arrays.stream(bidders).forEach(bidder -> biddersNode.set(bidder, dummyBidderConfigNode)); + + return mapper.createObjectNode() + .set("prebid", mapper.createObjectNode().set("bidder", biddersNode)); + } +} diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java similarity index 97% rename from extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java rename to extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java index a1ee31c8fca..967b637260a 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionMatchingRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java @@ -35,14 +35,14 @@ import static org.mockito.Mock.Strictness.LENIENT; @ExtendWith(MockitoExtension.class) -public class ConditionMatchingRuleTest { +public class ConditionalRuleTest { private final ObjectMapper mapper = new ObjectMapper(); private static final String ANALYTICS_KEY = "analyticsKey"; private static final String MODEL_VERSION = "modelVersion"; - private ConditionMatchingRule target; + private ConditionalRule target; @Mock(strictness = LENIENT) private Schema schema; @@ -61,7 +61,7 @@ public class ConditionMatchingRuleTest { @BeforeEach public void setUp() { - target = new ConditionMatchingRule<>(schema, ruleTree, ANALYTICS_KEY, MODEL_VERSION); + target = new ConditionalRule<>(schema, ruleTree, ANALYTICS_KEY, MODEL_VERSION); } @Test From 7a6e3d2cf724f5c8d8c92b04f6ca510d7dfec579 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 00:55:16 +0300 Subject: [PATCH 71/94] Added tests for logAtag function. --- .../result/functions/log/AnalyticsMapper.java | 2 +- .../functions/log/LogATagFunctionTest.java | 156 ++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java index 8cde93a47f9..dd42bfe34c6 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java @@ -18,7 +18,7 @@ public class AnalyticsMapper { - private static final String ACTIVITY_NAME = "rules-analytics-logger"; + private static final String ACTIVITY_NAME = "pb-rule-engine"; private static final String SUCCESS_STATUS = "success"; private AnalyticsMapper() { diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java new file mode 100644 index 00000000000..df107a4257a --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java @@ -0,0 +1,156 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.log; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.AppliedToImpl; +import org.prebid.server.hooks.execution.v1.analytics.ResultImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.util.ConfigurationValidationException; +import org.prebid.server.hooks.v1.analytics.Activity; +import org.prebid.server.hooks.v1.analytics.AppliedTo; +import org.prebid.server.hooks.v1.analytics.Result; +import org.prebid.server.hooks.v1.analytics.Tags; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LogATagFunctionTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final LogATagFunction target = new LogATagFunction(mapper); + + @Test + public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'analyticsValue' is required and has to be a string"); + } + + @Test + public void validateConfigShouldThrowErrorWhenAnalyticsValueFieldIsAbsent() { + // when and then + assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'analyticsValue' is required and has to be a string"); + } + + @Test + public void validateConfigShouldThrowErrorWhenAnalyticsValueFieldIsNotAString() { + // given + final ObjectNode config = mapper.createObjectNode().set("analyticsValue", mapper.createObjectNode()); + + // when and then + assertThatThrownBy(() -> target.validateConfig(config)) + .isInstanceOf(ConfigurationValidationException.class) + .hasMessage("Field 'analyticsValue' is required and has to be a string"); + } + + @Test + public void applyShouldEmitATagForRequestAndNotModifyOperand() { + // given + final RequestRuleContext context = RequestRuleContext.of( + AuctionContext.builder().build(), Granularity.Request.instance(), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + + final ObjectNode config = mapper.createObjectNode() + .set("analyticsValue", TextNode.valueOf("analyticsValue")); + + final ResultFunctionArguments resultFunctionArguments = + ResultFunctionArguments.of(BidRequest.builder().build(), config, infrastructureArguments); + + // when + final RuleResult result = target.apply(resultFunctionArguments); + + // then + final ObjectNode expectedValues = mapper.createObjectNode(); + expectedValues.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedValues.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedValues.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedValues.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedValues.set("resultFunction", TextNode.valueOf("logAtag")); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("*")).build(); + final Result expectedResult = ResultImpl.of("success", expectedValues, expectedAppliedTo); + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", "success", Collections.singletonList(expectedResult)); + final Tags expectedTags = TagsImpl.of(Collections.singletonList(expectedActivity)); + + assertThat(result).isEqualTo( + RuleResult.of( + BidRequest.builder().build(), + RuleAction.NO_ACTION, + expectedTags, + Collections.emptyList())); + } + + @Test + public void applyShouldEmitATagForImpAndNotModifyOperand() { + // given + final RequestRuleContext context = RequestRuleContext.of( + AuctionContext.builder().build(), new Granularity.Imp("impId"), null); + + final InfrastructureArguments infrastructureArguments = + InfrastructureArguments.builder() + .context(context) + .schemaFunctionResults(Collections.emptyMap()) + .schemaFunctionMatches(Collections.emptyMap()) + .ruleFired("ruleFired") + .analyticsKey("analyticsKey") + .modelVersion("modelVersion") + .build(); + + final ObjectNode config = mapper.createObjectNode() + .set("analyticsValue", TextNode.valueOf("analyticsValue")); + + final ResultFunctionArguments resultFunctionArguments = + ResultFunctionArguments.of(BidRequest.builder().build(), config, infrastructureArguments); + + // when + final RuleResult result = target.apply(resultFunctionArguments); + + // then + final ObjectNode expectedValues = mapper.createObjectNode(); + expectedValues.set("analyticsKey", TextNode.valueOf("analyticsKey")); + expectedValues.set("analyticsValue", TextNode.valueOf("analyticsValue")); + expectedValues.set("modelVersion", TextNode.valueOf("modelVersion")); + expectedValues.set("conditionFired", TextNode.valueOf("ruleFired")); + expectedValues.set("resultFunction", TextNode.valueOf("logAtag")); + + final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("impId")).build(); + final Result expectedResult = ResultImpl.of("success", expectedValues, expectedAppliedTo); + final Activity expectedActivity = ActivityImpl.of( + "pb-rule-engine", "success", Collections.singletonList(expectedResult)); + final Tags expectedTags = TagsImpl.of(Collections.singletonList(expectedActivity)); + + assertThat(result).isEqualTo( + RuleResult.of( + BidRequest.builder().build(), + RuleAction.NO_ACTION, + expectedTags, + Collections.emptyList())); + } +} From f93b7c3aa829dfc8dcdfa968ff192a3c416469c8 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 01:03:09 +0300 Subject: [PATCH 72/94] Added tests for AccountConfigParser. --- .../core/config/AccountConfigParserTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java new file mode 100644 index 00000000000..c4c4b12b725 --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java @@ -0,0 +1,62 @@ +package org.prebid.server.hooks.modules.rule.engine.core.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.BooleanNode; +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; + +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +public class AccountConfigParserTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private AccountConfigParser target; + + @Mock(strictness = LENIENT) + private StageConfigParser processedAuctionRequestStageParser; + + @BeforeEach + public void setUp() { + target = new AccountConfigParser(mapper, processedAuctionRequestStageParser); + } + + @Test + public void parseShouldReturnNoOpConfigWhenEnabledIsFalse() { + // when and then + assertThat(target.parse(mapper.createObjectNode().set("enabled", BooleanNode.getFalse()))).isEqualTo( + PerStageRule.builder() + .timestamp(Instant.EPOCH) + .processedAuctionRequestRule(NoOpRule.create()) + .build()); + } + + @Test + public void parseShouldParseRuleForEachSupportedStage() { + // given + final Rule rule = (Rule) mock(Rule.class); + given(processedAuctionRequestStageParser.parse(any())).willReturn(rule); + + // when and then + assertThat(target.parse(mapper.createObjectNode())).isEqualTo( + PerStageRule.builder() + .timestamp(Instant.EPOCH) + .processedAuctionRequestRule(rule) + .build()); + } +} From d4900f3b9350339e698bb207ff12c8dcb782cb72 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 01:18:47 +0300 Subject: [PATCH 73/94] Added tests for DefaultActionRule. --- .../core/rules/DefaultActionRuleTest.java | 154 +++++++++--------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java index b72bbac0899..d650a1a881f 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java @@ -1,78 +1,76 @@ -//package org.prebid.server.hooks.modules.rule.engine.core.rules; -// -//import org.junit.jupiter.api.Test; -//import org.mockito.stubbing.Answer; -//import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; -//import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; -//import org.prebid.server.hooks.modules.rule.engine.core.rules.result.RuleAction; -//import org.prebid.server.hooks.v1.analytics.Tags; -//import org.prebid.server.model.UpdateResult; -// -//import static java.util.Arrays.asList; -//import static java.util.Collections.emptyList; -//import static java.util.Collections.emptyMap; -//import static java.util.Collections.singletonList; -//import static org.assertj.core.api.Assertions.assertThat; -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.BDDMockito.given; -//import static org.mockito.Mockito.mock; -//import static org.mockito.Mockito.verify; -// -//public class DefaultActionRuleTest { -// -// private static final Object VALUE = new Object(); -// -// @Test -// public void processShouldAccumulateResultFromAllRuleActions() { -// // given -// final RuleAction firstAction = (RuleAction) mock(RuleAction.class); -// final ResultFunction firstFunction = (ResultFunction) mock(ResultFunction.class); -// -// given(firstFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("firstAction")); -// given(firstAction.getFunction()).willReturn(firstFunction); -// given(firstAction.getConfig()).willReturn(emptyList()); -// -// -// final RuleAction secondAction = (RuleAction) mock(RuleAction.class); -// final ResultFunction secondFunction = (ResultFunction) mock(ResultFunction.class); -// -// given(secondFunction.apply(any())).willAnswer(prepareResultFunctionAnswer("secondAction")); -// given(secondAction.getFunction()).willReturn(secondFunction); -// given(secondAction.getConfig()).willReturn(emptyList()); -// -// final DefaultActionRule target = new DefaultActionRule<>( -// asList(firstAction, secondAction), "analyticsKey", "modelVersion"); -// -// // when -// final RuleResult result = target.process(VALUE); -// -// // then -// final Tags expectedTags = TagsImpl.of( -// asList(ActivityImpl.of("firstAction", "success", emptyList()), -// ActivityImpl.of("secondAction", "success", emptyList()))); -// -// assertThat(result).isEqualTo(RuleResult.of(UpdateResult.updated(VALUE), expectedTags)); -// -// verify(firstFunction).apply( -// ResultFunctionArguments.of( -// VALUE, -// emptyList(), -// InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); -// verify(secondFunction).apply( -// ResultFunctionArguments.of( -// VALUE, -// emptyList(), -// InfrastructureArguments.of(emptyMap(), "analyticsKey", "default", "modelVersion"))); -// } -// -// private static Answer> prepareResultFunctionAnswer(String activityName) { -// return invocation -> RuleResult.of( -// UpdateResult.updated(((ResultFunctionArguments) invocation.getArgument(0)).getOperand()), -// TagsImpl.of(singletonList(ActivityImpl.of(activityName, "success", emptyList())))); -// } -//} +package org.prebid.server.hooks.modules.rule.engine.core.rules; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import org.junit.jupiter.api.Test; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.hooks.execution.v1.analytics.ActivityImpl; +import org.prebid.server.hooks.execution.v1.analytics.TagsImpl; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; +import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; + +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +public class DefaultActionRuleTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void processShouldAccumulateResultFromAllRuleActions() { + // given + final Object VALUE = new Object(); + final Object CONTEXT = new Object(); + + final ObjectNode firstConfig = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + final ResultFunction firstFunction = + (ResultFunction) mock(ResultFunction.class); + given(firstFunction.apply(any())).willAnswer(invocationOnMock -> RuleResult.of( + ((ResultFunctionArguments) invocationOnMock.getArgument(0)).getOperand(), + RuleAction.UPDATE, + TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))), + singletonList(SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))))); + + final ObjectNode secondConfig = mapper.createObjectNode().set("config", TextNode.valueOf("anotherTest")); + final ResultFunction secondFunction = + (ResultFunction) mock(ResultFunction.class); + given(secondFunction.apply(any())).willAnswer(invocationOnMock -> RuleResult.of( + ((ResultFunctionArguments) invocationOnMock.getArgument(0)).getOperand(), + RuleAction.UPDATE, + TagsImpl.of(singletonList(ActivityImpl.of("secondActivity", "success", emptyList()))), + singletonList(SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))))); + + final List> actions = List.of( + ResultFunctionHolder.of("firstFunction", firstFunction, firstConfig), + ResultFunctionHolder.of("secondFunction", secondFunction, secondConfig)); + + final DefaultActionRule target = new DefaultActionRule<>( + actions, "analyticsKey", "modelVersion"); + + // when + final RuleResult result = target.process(VALUE, CONTEXT); + + // then + final Tags expectedTags = TagsImpl.of( + asList(ActivityImpl.of("firstActivity", "success", emptyList()), + ActivityImpl.of("secondActivity", "success", emptyList()))); + + List expectedNonBids = List.of( + SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID))), + SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); + + assertThat(result).isEqualTo(RuleResult.of(VALUE, RuleAction.UPDATE, expectedTags, expectedNonBids)); + } +} From 2788a0ae6230fa10563e3fab2ad79b40203a983b Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 01:29:21 +0300 Subject: [PATCH 74/94] Added unit tests for RequestConditionalRuleFactory. --- .../RequestConditionalRuleFactoryTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactoryTest.java diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactoryTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactoryTest.java new file mode 100644 index 00000000000..f0e18b51fae --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestConditionalRuleFactoryTest.java @@ -0,0 +1,63 @@ +package org.prebid.server.hooks.modules.rule.engine.core.request; + +import com.iab.openrtb.request.BidRequest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleConfig; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; +import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.RuleTree; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ThreadLocalRandom; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +class RequestConditionalRuleFactoryTest { + + private final RequestConditionalRuleFactory target = new RequestConditionalRuleFactory(); + + @Mock(strictness = LENIENT) + SchemaFunction schemaFunction; + + @Mock(strictness = LENIENT) + RuleTree> ruleTree; + + @Test + void createShouldReturnConditionalRuleWhenSchemaDoesNotContainPerImpFunctions() { + // given + final Schema schema = Schema.of( + Collections.singletonList(SchemaFunctionHolder.of("function", schemaFunction, null))); + + // when and then + assertThat(target.create(schema, ruleTree, "key", "version")) + .isInstanceOf(ConditionalRule.class); + } + + @Test + void createShouldReturnPerImpConditionalRuleWhenSchemaContainPerImpFunctions() { + // given + final String perImpSchemaFunctionName = takeRandomElement(RequestStageSpecification.PER_IMP_SCHEMA_FUNCTIONS); + final Schema schema = Schema.of( + Collections.singletonList(SchemaFunctionHolder.of(perImpSchemaFunctionName, schemaFunction, null))); + + // when and then + assertThat(target.create(schema, ruleTree, "key", "version")) + .isInstanceOf(PerImpConditionalRule.class); + } + + private static T takeRandomElement(Collection collection) { + return collection + .stream() + .skip(ThreadLocalRandom.current().nextInt(collection.size())) + .findAny() + .orElse(null); + } +} From 6d2b4e551050c5fe99db22f6fa639f0af75527fd Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 02:04:02 +0300 Subject: [PATCH 75/94] Added unit tests for PbRuleEngineProcessedAuctionRequestHook. --- .../core/request/PerImpConditionalRule.java | 2 +- .../rule/engine/core/rules/CompositeRule.java | 2 +- .../engine/core/rules/ConditionalRule.java | 4 +- .../engine/core/rules/DefaultActionRule.java | 2 +- .../rule/engine/core/rules/NoOpRule.java | 2 +- .../rule/engine/core/rules/RuleResult.java | 2 +- ...RuleEngineProcessedAuctionRequestHook.java | 2 +- .../core/rules/AlternativeActionRuleTest.java | 7 +- .../engine/core/rules/WeightedRuleTest.java | 2 +- ...EngineProcessedAuctionRequestHookTest.java | 156 ++++++++++++++++++ 10 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java index 57332d677d7..093440440ad 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java @@ -18,7 +18,7 @@ public PerImpConditionalRule(ConditionalRule del @Override public RuleResult process(BidRequest value, RequestRuleContext context) { - RuleResult result = RuleResult.unaltered(value); + RuleResult result = RuleResult.noAction(value); for (Imp imp : value.getImp()) { result = result.mergeWith(delegate.process(result.getValue(), contextForImp(context, imp))); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java index 6d3aa0eaf3e..3c595960aac 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java @@ -11,7 +11,7 @@ public class CompositeRule implements Rule { @Override public RuleResult process(T value, C context) { - RuleResult result = RuleResult.unaltered(value); + RuleResult result = RuleResult.noAction(value); for (Rule subrule : subrules) { result = result.mergeWith(subrule.process(value, context)); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java index fe43742765e..74a1455a7c0 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java @@ -52,7 +52,7 @@ public RuleResult process(T value, C context) { try { lookupResult = ruleTree.lookup(matchers); } catch (NoMatchingRuleException e) { - return RuleResult.unaltered(value); + return RuleResult.noAction(value); } final RuleConfig ruleConfig = lookupResult.getValue(); @@ -67,7 +67,7 @@ public RuleResult process(T value, C context) { .modelVersion(modelVersion) .build(); - RuleResult result = RuleResult.unaltered(value); + RuleResult result = RuleResult.noAction(value); for (ResultFunctionHolder action : ruleConfig.getActions()) { result = result.mergeWith(applyAction(action, result.getValue(), infrastructureArguments)); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index d5d9e615570..3c2d2a7ed9d 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -29,7 +29,7 @@ public DefaultActionRule(List> actions, String analyt @Override public RuleResult process(T value, C context) { - RuleResult result = RuleResult.unaltered(value); + RuleResult result = RuleResult.noAction(value); for (ResultFunctionHolder action : actions) { result = result.mergeWith(applyAction(action, result.getValue(), context)); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java index 24814c8e188..c9ea02dc9f4 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/NoOpRule.java @@ -7,6 +7,6 @@ public class NoOpRule implements Rule { @Override public RuleResult process(T value, C context) { - return RuleResult.unaltered(value); + return RuleResult.noAction(value); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java index 6ddef5ea5ad..8967430982b 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RuleResult.java @@ -47,7 +47,7 @@ public boolean isUpdate() { return action == RuleAction.UPDATE; } - public static RuleResult unaltered(T value) { + public static RuleResult noAction(T value) { return RuleResult.of( value, RuleAction.NO_ACTION, TagsImpl.of(Collections.emptyList()), Collections.emptyList()); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index a1668df1438..005ef41072a 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -44,7 +44,7 @@ public Future> call(AuctionRequestPayloa final BidRequest bidRequest = auctionRequestPayload.bidRequest(); if (accountConfig == null) { - return succeeded(RuleResult.unaltered(bidRequest)); + return succeeded(RuleResult.noAction(bidRequest)); } return ruleParser.parseForAccount(accountId, accountConfig) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java index 8a46292709b..1a7d793dadf 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/AlternativeActionRuleTest.java @@ -5,9 +5,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import static org.assertj.core.api.Assertions.assertThat; @@ -19,8 +16,8 @@ @ExtendWith(MockitoExtension.class) public class AlternativeActionRuleTest { - private static final RuleResult DELEGATE_RESULT = RuleResult.unaltered(new Object()); - private static final RuleResult ALTERNATIVE_RESULT = RuleResult.unaltered(new Object()); + private static final RuleResult DELEGATE_RESULT = RuleResult.noAction(new Object()); + private static final RuleResult ALTERNATIVE_RESULT = RuleResult.noAction(new Object()); @Mock(strictness = LENIENT) private Rule delegate; diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java index 2b77d7f89ee..b9304f40b55 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/WeightedRuleTest.java @@ -17,7 +17,7 @@ public void processShouldUtilizeRuleFromWeightedList() { // given final WeightedList> ruleList = (WeightedList>) mock(WeightedList.class); - final RuleResult stub = RuleResult.unaltered(new Object()); + final RuleResult stub = RuleResult.noAction(new Object()); given(ruleList.getForSeed(anyInt())).willReturn((left, right) -> stub); final RandomGenerator randomGenerator = mock(RandomGenerator.class); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java new file mode 100644 index 00000000000..0801917c7ad --- /dev/null +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java @@ -0,0 +1,156 @@ +package org.prebid.server.hooks.modules.rule.engine.v1; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.iab.openrtb.request.BidRequest; +import io.vertx.core.Future; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; +import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; +import org.prebid.server.hooks.modules.rule.engine.core.request.RequestRuleContext; +import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleAction; +import org.prebid.server.hooks.modules.rule.engine.core.rules.RuleResult; +import org.prebid.server.hooks.v1.InvocationAction; +import org.prebid.server.hooks.v1.InvocationStatus; +import org.prebid.server.hooks.v1.analytics.Tags; +import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; +import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; +import org.prebid.server.settings.model.Account; + +import java.time.Instant; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; + +@ExtendWith(MockitoExtension.class) +class PbRuleEngineProcessedAuctionRequestHookTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + + private PbRuleEngineProcessedAuctionRequestHook target; + + @Mock(strictness = LENIENT) + private RuleParser ruleParser; + + @Mock(strictness = LENIENT) + private AuctionRequestPayload payload; + + @Mock(strictness = LENIENT) + private AuctionInvocationContext invocationContext; + + @Mock(strictness = LENIENT) + private Rule processedAuctionRequestRule; + + @Mock(strictness = LENIENT) + private BidRequest bidRequest; + + @Mock(strictness = LENIENT) + private Tags tags; + + private final AuctionContext auctionContext = AuctionContext.builder().account(Account.empty("1001")).build(); + + @BeforeEach + void setUp() { + target = new PbRuleEngineProcessedAuctionRequestHook(ruleParser, "datacenter"); + + given(invocationContext.auctionContext()).willReturn(auctionContext); + given(payload.bidRequest()).willReturn(bidRequest); + + given(ruleParser.parseForAccount(any(), any())).willReturn( + Future.succeededFuture( + PerStageRule.builder() + .timestamp(Instant.EPOCH) + .processedAuctionRequestRule(processedAuctionRequestRule) + .build())); + } + + @Test + public void callShouldReturnNoActionWhenNoAccountConfigProvided() { + // when and then + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { + assertThat(invocationResult.status()).isEqualTo(InvocationStatus.success); + assertThat(invocationResult.action()).isEqualTo(InvocationAction.no_action); + assertThat(invocationResult.payloadUpdate()).isNull(); + }); + } + + @Test + public void callShouldReturnNoActionWhenRuleActionIsNoAction() { + // given + given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(processedAuctionRequestRule.process( + bidRequest, + RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) + .willReturn(RuleResult.noAction(bidRequest)); + + // when and then + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { + assertThat(invocationResult.status()).isEqualTo(InvocationStatus.success); + assertThat(invocationResult.action()).isEqualTo(InvocationAction.no_action); + assertThat(invocationResult.payloadUpdate()).isNull(); + }); + } + + @Test + public void callShouldReturnPayloadUpdateWhenRuleActionIsUpdate() { + // given + given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(processedAuctionRequestRule.process( + bidRequest, + RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) + .willReturn(RuleResult.of(bidRequest, RuleAction.UPDATE, tags, Collections.emptyList())); + + // when and then + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { + assertThat(invocationResult.status()).isEqualTo(InvocationStatus.success); + assertThat(invocationResult.action()).isEqualTo(InvocationAction.update); + assertThat(invocationResult.payloadUpdate()).isNotNull(); + assertThat(invocationResult.analyticsTags()).isEqualTo(tags); + }); + } + + @Test + public void callShouldReturnRejectWhenRuleActionIsReject() { + // given + given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(processedAuctionRequestRule.process( + bidRequest, + RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) + .willReturn(RuleResult.rejected(tags, Collections.emptyList())); + + // when and then + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { + assertThat(invocationResult.status()).isEqualTo(InvocationStatus.success); + assertThat(invocationResult.action()).isEqualTo(InvocationAction.reject); + assertThat(invocationResult.payloadUpdate()).isNull(); + assertThat(invocationResult.analyticsTags()).isEqualTo(tags); + }); + } + + @Test + public void callShouldReturnFailureOnFailure() { + // given + given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(processedAuctionRequestRule.process( + bidRequest, + RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) + .willThrow(PreBidException.class); + + // when and then + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { + assertThat(invocationResult.status()).isEqualTo(InvocationStatus.failure); + assertThat(invocationResult.action()).isEqualTo(InvocationAction.no_invocation); + assertThat(invocationResult.payloadUpdate()).isNull(); + }); + } +} From aa8fb63db31eab86888956bf155aaa57637155fb Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 20 Aug 2025 02:25:17 +0300 Subject: [PATCH 76/94] Fixed styling. --- .../PbRuleEngineModuleConfiguration.java | 1 + .../rule/engine/core/request/Granularity.java | 3 + .../core/request/PerImpConditionalRule.java | 3 +- .../functions/filter/AnalyticsMapper.java | 1 - .../filter/FilterBiddersFunction.java | 6 +- .../functions/filter/FilterBiddersResult.java | 1 - .../result/functions/filter/FilterUtils.java | 2 +- .../result/functions/log/AnalyticsMapper.java | 1 - .../functions/EidAvailableFunction.java | 2 - .../schema/functions/MediaTypeInFunction.java | 2 +- .../rule/engine/core/rules/CompositeRule.java | 3 +- .../engine/core/rules/ConditionalRule.java | 8 +-- .../engine/core/rules/DefaultActionRule.java | 3 +- .../rule/engine/core/rules/schema/Schema.java | 1 - .../rule/engine/core/rules/tree/RuleTree.java | 2 +- .../core/rules/tree/RuleTreeFactory.java | 3 + .../engine/core/util/ValidationUtils.java | 3 + ...RuleEngineProcessedAuctionRequestHook.java | 2 +- .../core/config/AccountConfigParserTest.java | 8 +-- .../filter/ExcludeBiddersFunctionTest.java | 60 +++++++++---------- .../filter/IncludeBiddersFunctionTest.java | 59 +++++++++--------- .../functions/log/LogATagFunctionTest.java | 18 +++--- .../functions/AdUnitCodeFunctionTest.java | 16 ++--- .../functions/AdUnitCodeInFunctionTest.java | 41 +++++++------ .../schema/functions/BundleFunctionTest.java | 4 +- .../functions/BundleInFunctionTest.java | 16 ++--- .../schema/functions/ChannelFunctionTest.java | 4 +- .../functions/DataCenterFunctionTest.java | 4 +- .../functions/DataCenterInFunctionTest.java | 16 ++--- .../functions/DeviceCountryFunctionTest.java | 4 +- .../DeviceCountryInFunctionTest.java | 16 ++--- .../functions/DeviceTypeFunctionTest.java | 4 +- .../functions/DeviceTypeInFunctionTest.java | 16 ++--- .../schema/functions/DomainFunctionTest.java | 4 +- .../functions/DomainInFunctionTest.java | 16 ++--- .../functions/EidAvailableFunctionTest.java | 4 +- .../schema/functions/EidInFunctionTest.java | 16 ++--- .../functions/FpdAvailableFunctionTest.java | 10 ++-- .../GppSidAvailableFunctionTest.java | 4 +- .../functions/GppSidInFunctionTest.java | 18 +++--- .../functions/MediaTypeInFunctionTest.java | 17 +++--- .../functions/PrebidKeyFunctionTest.java | 12 ++-- .../functions/TcfInScopeFunctionTest.java | 4 +- .../UserFpdAvailableFunctionTest.java | 6 +- .../engine/core/rules/CompositeRuleTest.java | 2 +- .../core/rules/ConditionalRuleTest.java | 10 ++-- .../core/rules/DefaultActionRuleTest.java | 16 ++--- ...EngineProcessedAuctionRequestHookTest.java | 10 ++-- 48 files changed, 240 insertions(+), 242 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java index ae9a0df11f0..2caf6ee8b30 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/config/PbRuleEngineModuleConfiguration.java @@ -36,6 +36,7 @@ PbRuleEngineModule ruleEngineModule(RuleParser ruleParser, @Bean StageConfigParser processedAuctionRequestStageParser( BidderCatalog bidderCatalog) { + final RandomGenerator randomGenerator = () -> ThreadLocalRandom.current().nextLong(); return new StageConfigParser<>( diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java index 1fc1debd6ee..e820c787a71 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/Granularity.java @@ -5,6 +5,9 @@ public sealed interface Granularity { final class Request implements Granularity { private static final Request INSTANCE = new Request(); + private Request() { + } + public static Request instance() { return INSTANCE; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java index 093440440ad..5b644c8cde5 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/PerImpConditionalRule.java @@ -22,8 +22,9 @@ public RuleResult process(BidRequest value, RequestRuleContext conte for (Imp imp : value.getImp()) { result = result.mergeWith(delegate.process(result.getValue(), contextForImp(context, imp))); - if (result.isReject()) + if (result.isReject()) { return result; + } } return result; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java index e97ceeceba7..0f20384d401 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/AnalyticsMapper.java @@ -74,7 +74,6 @@ public static Tags toTags(ObjectMapper mapper, ActivityImpl.of(ACTIVITY_NAME, SUCCESS_STATUS, Collections.singletonList(result)))); } - private record AnalyticsData(@JsonProperty("analyticsKey") String analyticsKey, @JsonProperty("analyticsValue") String analyticsValue, @JsonProperty("modelVersion") String modelVersion, diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 2f74db2eae2..84886c4f677 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -55,8 +55,8 @@ public RuleResult apply(ResultFunctionArguments seatNonBid = new ArrayList<>(); for (Imp imp : bidRequest.getImp()) { - if (granularity instanceof Granularity.Imp && - !StringUtils.equals(((Granularity.Imp) granularity).impId(), imp.getId())) { + if (granularity instanceof Granularity.Imp + && !StringUtils.equals(((Granularity.Imp) granularity).impId(), imp.getId())) { updatedImps.add(imp); continue; @@ -86,7 +86,7 @@ public RuleResult apply(ResultFunctionArguments { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index 79d0074e893..547efb749ee 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -37,7 +37,7 @@ public String extract(SchemaFunctionArguments ar final Set adUnitMediaTypes = adUnitMediaTypes(adUnit); - boolean intersects = StreamUtil.asStream(arguments.getConfig().get(TYPES_FIELD).elements()) + final boolean intersects = StreamUtil.asStream(arguments.getConfig().get(TYPES_FIELD).elements()) .map(JsonNode::asText) .anyMatch(adUnitMediaTypes::contains); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java index 3c595960aac..ca922f1720a 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRule.java @@ -16,8 +16,9 @@ public RuleResult process(T value, C context) { for (Rule subrule : subrules) { result = result.mergeWith(subrule.process(value, context)); - if (result.isReject()) + if (result.isReject()) { return result; + } } return result; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java index 74a1455a7c0..6bd5de9ef03 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java @@ -6,6 +6,7 @@ import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.Schema; +import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunctionHolder; import org.prebid.server.hooks.modules.rule.engine.core.rules.tree.LookupResult; @@ -17,8 +18,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.prebid.server.hooks.modules.rule.engine.core.rules.schema.SchemaFunction.UNDEFINED_RESULT; - public class ConditionalRule implements Rule { private final Schema schema; @@ -45,7 +44,7 @@ public RuleResult process(T value, C context) { final List matchers = schemaFunctions.stream() .map(holder -> holder.getSchemaFunction().extract( SchemaFunctionArguments.of(value, holder.getConfig(), context))) - .map(matcher -> StringUtils.defaultIfEmpty(matcher, UNDEFINED_RESULT)) + .map(matcher -> StringUtils.defaultIfEmpty(matcher, SchemaFunction.UNDEFINED_RESULT)) .toList(); final LookupResult> lookupResult; @@ -71,8 +70,9 @@ public RuleResult process(T value, C context) { for (ResultFunctionHolder action : ruleConfig.getActions()) { result = result.mergeWith(applyAction(action, result.getValue(), infrastructureArguments)); - if (result.isReject()) + if (result.isReject()) { return result; + } } return result; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java index 3c2d2a7ed9d..5e3198b8e24 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRule.java @@ -34,8 +34,9 @@ public RuleResult process(T value, C context) { for (ResultFunctionHolder action : actions) { result = result.mergeWith(applyAction(action, result.getValue(), context)); - if (result.isReject()) + if (result.isReject()) { return result; + } } return result; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java index 5c725f23e77..64ec5aa6e23 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/schema/Schema.java @@ -3,7 +3,6 @@ import lombok.Value; import java.util.List; -import java.util.Set; @Value(staticConstructor = "of") public class Schema { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java index a5e54ac5e12..6473bd5620f 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTree.java @@ -10,7 +10,7 @@ public class RuleTree { - public static String WILDCARD_MATCHER = "*"; + public static final String WILDCARD_MATCHER = "*"; private final RuleNode root; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 0e6406f9b7c..1e378ef74fe 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -10,6 +10,9 @@ public class RuleTreeFactory { + private RuleTreeFactory() { + } + public static RuleTree> buildTree(List> rules) { final List>> parsingContexts = toParsingContexts(rules); final int depth = getDepth(parsingContexts); diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java index 4b040c263b4..b95663dfea4 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/ValidationUtils.java @@ -10,6 +10,9 @@ public class ValidationUtils { + private ValidationUtils() { + } + public static void assertArrayOfStrings(ObjectNode config, String fieldName) { assertArrayOf( config, diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index 005ef41072a..5199ed15b48 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -66,7 +66,7 @@ private static Future> succeeded(RuleRes resultBuilder.payloadUpdate(initialPayload -> AuctionRequestPayloadImpl.of(result.getValue())); } - return Future.succeededFuture(resultBuilder.build() ); + return Future.succeededFuture(resultBuilder.build()); } private static Future> failure(Throwable error) { diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java index c4c4b12b725..fe7a33b5a56 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/AccountConfigParserTest.java @@ -24,7 +24,7 @@ @ExtendWith(MockitoExtension.class) public class AccountConfigParserTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private AccountConfigParser target; @@ -33,13 +33,13 @@ public class AccountConfigParserTest { @BeforeEach public void setUp() { - target = new AccountConfigParser(mapper, processedAuctionRequestStageParser); + target = new AccountConfigParser(MAPPER, processedAuctionRequestStageParser); } @Test public void parseShouldReturnNoOpConfigWhenEnabledIsFalse() { // when and then - assertThat(target.parse(mapper.createObjectNode().set("enabled", BooleanNode.getFalse()))).isEqualTo( + assertThat(target.parse(MAPPER.createObjectNode().set("enabled", BooleanNode.getFalse()))).isEqualTo( PerStageRule.builder() .timestamp(Instant.EPOCH) .processedAuctionRequestRule(NoOpRule.create()) @@ -53,7 +53,7 @@ public void parseShouldParseRuleForEachSupportedStage() { given(processedAuctionRequestStageParser.parse(any())).willReturn(rule); // when and then - assertThat(target.parse(mapper.createObjectNode())).isEqualTo( + assertThat(target.parse(MAPPER.createObjectNode())).isEqualTo( PerStageRule.builder() .timestamp(Instant.EPOCH) .processedAuctionRequestRule(rule) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java index bdee427ed3c..18b13c413a5 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunctionTest.java @@ -52,7 +52,7 @@ @ExtendWith(MockitoExtension.class) class ExcludeBiddersFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private ExcludeBiddersFunction target; @@ -61,7 +61,7 @@ class ExcludeBiddersFunctionTest { @BeforeEach void setUp() { - target = new ExcludeBiddersFunction(mapper, bidderCatalog); + target = new ExcludeBiddersFunction(MAPPER, bidderCatalog); } @Test @@ -75,7 +75,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { // given - final ObjectNode config = mapper.createObjectNode().set("bidders", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("bidders", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -85,7 +85,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { @Test public void validateConfigShouldThrowErrorWhenBiddersFieldIsEmpty() { // given - final ObjectNode config = mapper.createObjectNode(); + final ObjectNode config = MAPPER.createObjectNode(); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -95,7 +95,7 @@ public void validateConfigShouldThrowErrorWhenBiddersFieldIsEmpty() { @Test void applyShouldExcludeBiddersSpecifiedInConfigAndEmitSeatNonBidsWithATags() { - // given + // given final BidRequest bidRequest = givenBidRequest(givenImp("impId", "bidder1", "bidder2")); final RequestRuleContext context = RequestRuleContext.of( @@ -111,19 +111,19 @@ void applyShouldExcludeBiddersSpecifiedInConfigAndEmitSeatNonBidsWithATags() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder1")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder1")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("impId")).build(); @@ -164,19 +164,19 @@ void applyShouldExcludeBiddersSpecifiedInConfigOnlyForSpecifiedImpWhenGranularit .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder3")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -224,19 +224,19 @@ void applyShouldExcludeBiddersWithoutLiveUidSpecifiedInConfigWhenIfSyncedIdSetTo .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder2")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -280,19 +280,19 @@ void applyShouldExcludeBiddersWitLiveUidSpecifiedInConfigWhenIfSyncedIdSetToTrue .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder2")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -336,19 +336,19 @@ void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3").add("bidder4")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder3").add("bidder4")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -360,7 +360,6 @@ void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { "success", Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); - final List expectedSeatNonBid = List.of( SeatNonBid.of( "bidder3", @@ -399,19 +398,19 @@ void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("excludeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -423,7 +422,6 @@ void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { "success", Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); - final List expectedSeatNonBid = List.of( SeatNonBid.of( "bidder", @@ -457,7 +455,7 @@ void applyShouldNotGenerateATagWhenNoAnalyticsKeySpecified() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); @@ -498,7 +496,7 @@ private AuctionContext givenAuctionContext(String... liveUidBidders) { .collect(Collectors.toMap(Function.identity(), ignored -> UidWithExpiry.live("uid"))); final UidsCookie uidsCookie = new UidsCookie( - Uids.builder().uids(uids).build(), new JacksonMapper(mapper)); + Uids.builder().uids(uids).build(), new JacksonMapper(MAPPER)); Arrays.stream(liveUidBidders).forEach( bidder -> given(bidderCatalog.cookieFamilyName(bidder)).willReturn(Optional.of(bidder))); @@ -517,11 +515,11 @@ private static Imp givenImp(String impId, String... bidders) { } private static ObjectNode givenImpExt(String... bidders) { - final ObjectNode biddersNode = mapper.createObjectNode(); - final ObjectNode dummyBidderConfigNode = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + final ObjectNode biddersNode = MAPPER.createObjectNode(); + final ObjectNode dummyBidderConfigNode = MAPPER.createObjectNode().set("config", TextNode.valueOf("test")); Arrays.stream(bidders).forEach(bidder -> biddersNode.set(bidder, dummyBidderConfigNode)); - return mapper.createObjectNode() - .set("prebid", mapper.createObjectNode().set("bidder", biddersNode)); + return MAPPER.createObjectNode() + .set("prebid", MAPPER.createObjectNode().set("bidder", biddersNode)); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java index 57507717260..f8cc0aeab1a 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunctionTest.java @@ -52,7 +52,7 @@ @ExtendWith(MockitoExtension.class) class IncludeBiddersFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private IncludeBiddersFunction target; @@ -61,7 +61,7 @@ class IncludeBiddersFunctionTest { @BeforeEach void setUp() { - target = new IncludeBiddersFunction(mapper, bidderCatalog); + target = new IncludeBiddersFunction(MAPPER, bidderCatalog); } @Test @@ -75,7 +75,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { // given - final ObjectNode config = mapper.createObjectNode().set("bidders", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("bidders", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -85,7 +85,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsInvalid() { @Test public void validateConfigShouldThrowErrorWhenBiddersFieldIsEmpty() { // given - final ObjectNode config = mapper.createObjectNode(); + final ObjectNode config = MAPPER.createObjectNode(); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -111,19 +111,19 @@ void applyShouldExcludeBiddersNotSpecifiedInConfigAndEmitSeatNonBidsWithATags() .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder2")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder().impIds(Collections.singletonList("impId")).build(); @@ -164,19 +164,19 @@ void applyShouldExcludeBiddersNotSpecifiedInConfigOnlyForSpecifiedImpWhenGranula .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder4")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder4")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -204,7 +204,6 @@ void applyShouldExcludeBiddersNotSpecifiedInConfigOnlyForSpecifiedImpWhenGranula Collections.singletonList(expectedSeatNonBid))); } - @Test void applyShouldExcludeBiddersWithLiveUidOrNotSpecifiedInConfigWhenIfSyncedIdSetToFalse() { // given @@ -225,19 +224,19 @@ void applyShouldExcludeBiddersWithLiveUidOrNotSpecifiedInConfigWhenIfSyncedIdSet .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder1")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder1")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -281,19 +280,19 @@ void applyShouldExcludeBiddersWithoutLiveUidOrNotSpecifiedInConfigWhenIfSyncedId .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder2")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder2")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -337,19 +336,19 @@ void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder3").add("bidder4")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder3").add("bidder4")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -361,7 +360,6 @@ void applyShouldDiscardImpIfAfterUpdateImpExtHasNoBidders() { "success", Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); - final List expectedSeatNonBid = List.of( SeatNonBid.of( "bidder3", @@ -400,19 +398,19 @@ void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); // then - final ObjectNode expectedResultValue = mapper.createObjectNode(); + final ObjectNode expectedResultValue = MAPPER.createObjectNode(); expectedResultValue.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedResultValue.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedResultValue.set("modelVersion", TextNode.valueOf("modelVersion")); expectedResultValue.set("conditionFired", TextNode.valueOf("ruleFired")); expectedResultValue.set("resultFunction", TextNode.valueOf("includeBidders")); - expectedResultValue.set("biddersRemoved", mapper.createArrayNode().add("bidder")); + expectedResultValue.set("biddersRemoved", MAPPER.createArrayNode().add("bidder")); expectedResultValue.set("seatNonBid", IntNode.valueOf(BidRejectionReason.REQUEST_BLOCKED_GENERAL.getValue())); final AppliedTo expectedAppliedTo = AppliedToImpl.builder() @@ -424,7 +422,6 @@ void applyShouldRejectBidRequestIfUpdatedRequestHasNoImps() { "success", Collections.singletonList(ResultImpl.of("success", expectedResultValue, expectedAppliedTo))); - final List expectedSeatNonBid = List.of( SeatNonBid.of( "bidder", @@ -458,7 +455,7 @@ void applyShouldNotGenerateATagWhenNoAnalyticsKeySpecified() { .build(); final ResultFunctionArguments arguments = - ResultFunctionArguments.of(bidRequest, mapper.valueToTree(config), infrastructureArguments); + ResultFunctionArguments.of(bidRequest, MAPPER.valueToTree(config), infrastructureArguments); // when final RuleResult result = target.apply(arguments); @@ -499,7 +496,7 @@ private AuctionContext givenAuctionContext(String... liveUidBidders) { .collect(Collectors.toMap(Function.identity(), ignored -> UidWithExpiry.live("uid"))); final UidsCookie uidsCookie = new UidsCookie( - Uids.builder().uids(uids).build(), new JacksonMapper(mapper)); + Uids.builder().uids(uids).build(), new JacksonMapper(MAPPER)); Arrays.stream(liveUidBidders).forEach( bidder -> given(bidderCatalog.cookieFamilyName(bidder)).willReturn(Optional.of(bidder))); @@ -518,11 +515,11 @@ private static Imp givenImp(String impId, String... bidders) { } private static ObjectNode givenImpExt(String... bidders) { - final ObjectNode biddersNode = mapper.createObjectNode(); - final ObjectNode dummyBidderConfigNode = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + final ObjectNode biddersNode = MAPPER.createObjectNode(); + final ObjectNode dummyBidderConfigNode = MAPPER.createObjectNode().set("config", TextNode.valueOf("test")); Arrays.stream(bidders).forEach(bidder -> biddersNode.set(bidder, dummyBidderConfigNode)); - return mapper.createObjectNode() - .set("prebid", mapper.createObjectNode().set("bidder", biddersNode)); + return MAPPER.createObjectNode() + .set("prebid", MAPPER.createObjectNode().set("bidder", biddersNode)); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java index df107a4257a..92ac210d950 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/LogATagFunctionTest.java @@ -29,14 +29,14 @@ class LogATagFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); - private final LogATagFunction target = new LogATagFunction(mapper); + private final LogATagFunction target = new LogATagFunction(MAPPER); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'analyticsValue' is required and has to be a string"); } @@ -44,7 +44,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenAnalyticsValueFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'analyticsValue' is required and has to be a string"); } @@ -52,7 +52,7 @@ public void validateConfigShouldThrowErrorWhenAnalyticsValueFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenAnalyticsValueFieldIsNotAString() { // given - final ObjectNode config = mapper.createObjectNode().set("analyticsValue", mapper.createObjectNode()); + final ObjectNode config = MAPPER.createObjectNode().set("analyticsValue", MAPPER.createObjectNode()); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -76,7 +76,7 @@ public void applyShouldEmitATagForRequestAndNotModifyOperand() { .modelVersion("modelVersion") .build(); - final ObjectNode config = mapper.createObjectNode() + final ObjectNode config = MAPPER.createObjectNode() .set("analyticsValue", TextNode.valueOf("analyticsValue")); final ResultFunctionArguments resultFunctionArguments = @@ -86,7 +86,7 @@ public void applyShouldEmitATagForRequestAndNotModifyOperand() { final RuleResult result = target.apply(resultFunctionArguments); // then - final ObjectNode expectedValues = mapper.createObjectNode(); + final ObjectNode expectedValues = MAPPER.createObjectNode(); expectedValues.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedValues.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedValues.set("modelVersion", TextNode.valueOf("modelVersion")); @@ -123,7 +123,7 @@ public void applyShouldEmitATagForImpAndNotModifyOperand() { .modelVersion("modelVersion") .build(); - final ObjectNode config = mapper.createObjectNode() + final ObjectNode config = MAPPER.createObjectNode() .set("analyticsValue", TextNode.valueOf("analyticsValue")); final ResultFunctionArguments resultFunctionArguments = @@ -133,7 +133,7 @@ public void applyShouldEmitATagForImpAndNotModifyOperand() { final RuleResult result = target.apply(resultFunctionArguments); // then - final ObjectNode expectedValues = mapper.createObjectNode(); + final ObjectNode expectedValues = MAPPER.createObjectNode(); expectedValues.set("analyticsKey", TextNode.valueOf("analyticsKey")); expectedValues.set("analyticsValue", TextNode.valueOf("analyticsValue")); expectedValues.set("modelVersion", TextNode.valueOf("modelVersion")); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java index 97873c845c8..f129e12eeed 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeFunctionTest.java @@ -18,14 +18,14 @@ public class AdUnitCodeFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final AdUnitCodeFunction target = new AdUnitCodeFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -38,7 +38,7 @@ public void extractShouldReturnGpidWhenPresent() { // given final Imp imp = Imp.builder() .id("impId") - .ext(mapper.createObjectNode().put("gpid", "gpid")) + .ext(MAPPER.createObjectNode().put("gpid", "gpid")) .build(); final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); @@ -68,8 +68,8 @@ public void extractShouldReturnTagidWhenGpidAbsentAndTagidPresent() { @Test public void extractShouldReturnPbAdSlotWhenGpidAndTagidAreAbsent() { // given - final ObjectNode ext = mapper.createObjectNode(); - ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + final ObjectNode ext = MAPPER.createObjectNode(); + ext.set("data", MAPPER.createObjectNode().put("pbadslot", "pbadslot")); final Imp imp = Imp.builder().id("impId").ext(ext).build(); @@ -84,9 +84,9 @@ public void extractShouldReturnPbAdSlotWhenGpidAndTagidAreAbsent() { @Test public void extractShouldReturnStoredRequestIdWhenGpidAndTagidAndPbAdSlotAreAbsent() { // given - final ObjectNode prebid = mapper.createObjectNode(); - prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); - final ObjectNode ext = mapper.createObjectNode(); + final ObjectNode prebid = MAPPER.createObjectNode(); + prebid.set("storedrequest", MAPPER.createObjectNode().put("id", "srid")); + final ObjectNode ext = MAPPER.createObjectNode(); ext.set("prebid", prebid); final Imp imp = Imp.builder().id("impId").ext(ext).build(); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java index 3fe896539ee..d3705031a51 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/AdUnitCodeInFunctionTest.java @@ -22,14 +22,14 @@ public class AdUnitCodeInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final AdUnitCodeInFunction target = new AdUnitCodeInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'codes' is required and has to be an array of strings"); } @@ -37,7 +37,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenCodesFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'codes' is required and has to be an array of strings"); } @@ -45,7 +45,7 @@ public void validateConfigShouldThrowErrorWhenCodesFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenCodesFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("codes", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("codes", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -56,10 +56,10 @@ public void validateConfigShouldThrowErrorWhenCodesFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenCodesFieldIsNotAnArrayOfStrings() { // given - final ArrayNode codesNode = mapper.createArrayNode(); + final ArrayNode codesNode = MAPPER.createArrayNode(); codesNode.add(TextNode.valueOf("test")); codesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("codes", codesNode); + final ObjectNode config = MAPPER.createObjectNode().set("codes", codesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -72,7 +72,7 @@ public void extractShouldReturnTrueWhenGpidPresentInConfiguredCodes() { // given final Imp imp = Imp.builder() .id("impId") - .ext(mapper.createObjectNode().put("gpid", "gpid")) + .ext(MAPPER.createObjectNode().put("gpid", "gpid")) .build(); final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); @@ -90,7 +90,7 @@ public void extractShouldReturnTrueWhenTagidPresentAndGpidIsAbsentInConfiguredCo final Imp imp = Imp.builder() .id("impId") .tagid("tagId") - .ext(mapper.createObjectNode().put("gpid", "gpid")) + .ext(MAPPER.createObjectNode().put("gpid", "gpid")) .build(); final BidRequest bidRequest = BidRequest.builder().imp(singletonList(imp)).build(); @@ -105,8 +105,8 @@ public void extractShouldReturnTrueWhenTagidPresentAndGpidIsAbsentInConfiguredCo @Test public void extractShouldReturnTrueWhenPbAdSlotPresentAndGpidAndTagidAreAbsentInConfiguredCodes() { // given - final ObjectNode ext = mapper.createObjectNode(); - ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + final ObjectNode ext = MAPPER.createObjectNode(); + ext.set("data", MAPPER.createObjectNode().put("pbadslot", "pbadslot")); ext.set("gpid", TextNode.valueOf("gpid")); final Imp imp = Imp.builder() @@ -127,11 +127,11 @@ public void extractShouldReturnTrueWhenPbAdSlotPresentAndGpidAndTagidAreAbsentIn @Test public void extractShouldReturnTrueWhenSridPresentAndGpidAndTagidAndPbAdSlotAreAbsentInConfiguredCodes() { // given - final ObjectNode prebid = mapper.createObjectNode(); - prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); - final ObjectNode ext = mapper.createObjectNode(); + final ObjectNode prebid = MAPPER.createObjectNode(); + prebid.set("storedrequest", MAPPER.createObjectNode().put("id", "srid")); + final ObjectNode ext = MAPPER.createObjectNode(); ext.set("prebid", prebid); - ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + ext.set("data", MAPPER.createObjectNode().put("pbadslot", "pbadslot")); ext.set("gpid", TextNode.valueOf("gpid")); final Imp imp = Imp.builder() @@ -152,11 +152,11 @@ public void extractShouldReturnTrueWhenSridPresentAndGpidAndTagidAndPbAdSlotAreA @Test public void extractShouldReturnFalseWhenAdUnitCodesDoesNotMatchConfiguredCodes() { // given - final ObjectNode prebid = mapper.createObjectNode(); - prebid.set("storedrequest", mapper.createObjectNode().put("id", "srid")); - final ObjectNode ext = mapper.createObjectNode(); + final ObjectNode prebid = MAPPER.createObjectNode(); + prebid.set("storedrequest", MAPPER.createObjectNode().put("id", "srid")); + final ObjectNode ext = MAPPER.createObjectNode(); ext.set("prebid", prebid); - ext.set("data", mapper.createObjectNode().put("pbadslot", "pbadslot")); + ext.set("data", MAPPER.createObjectNode().put("pbadslot", "pbadslot")); ext.set("gpid", TextNode.valueOf("gpid")); final Imp imp = Imp.builder() @@ -174,7 +174,6 @@ public void extractShouldReturnFalseWhenAdUnitCodesDoesNotMatchConfiguredCodes() assertThat(target.extract(arguments)).isEqualTo("false"); } - private SchemaFunctionArguments givenFunctionArguments( BidRequest bidRequest, String... codes) { @@ -186,8 +185,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithCodes(String... codes) { - final ArrayNode codesNode = mapper.createArrayNode(); + final ArrayNode codesNode = MAPPER.createArrayNode(); Arrays.stream(codes).map(TextNode::valueOf).forEach(codesNode::add); - return mapper.createObjectNode().set("codes", codesNode); + return MAPPER.createObjectNode().set("codes", codesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java index ad62d55c811..8059c530cc8 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleFunctionTest.java @@ -17,14 +17,14 @@ public class BundleFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final BundleFunction target = new BundleFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java index 8a71982c30c..4a16a45fae8 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/BundleInFunctionTest.java @@ -21,14 +21,14 @@ public class BundleInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final BundleInFunction target = new BundleInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'bundles' is required and has to be an array of strings"); } @@ -36,7 +36,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'bundles' is required and has to be an array of strings"); } @@ -44,7 +44,7 @@ public void validateConfigShouldThrowErrorWhenBundlesFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("bundles", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("bundles", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -55,10 +55,10 @@ public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenBundlesFieldIsNotAnArrayOfStrings() { // given - final ArrayNode bundlesNode = mapper.createArrayNode(); + final ArrayNode bundlesNode = MAPPER.createArrayNode(); bundlesNode.add(TextNode.valueOf("test")); bundlesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("bundles", bundlesNode); + final ObjectNode config = MAPPER.createObjectNode().set("bundles", bundlesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -105,8 +105,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithBundles(String... bundles) { - final ArrayNode bundlesNode = mapper.createArrayNode(); + final ArrayNode bundlesNode = MAPPER.createArrayNode(); Arrays.stream(bundles).map(TextNode::valueOf).forEach(bundlesNode::add); - return mapper.createObjectNode().set("bundles", bundlesNode); + return MAPPER.createObjectNode().set("bundles", bundlesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java index 75fb617323f..c310678e178 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/ChannelFunctionTest.java @@ -19,14 +19,14 @@ public class ChannelFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final ChannelFunction target = new ChannelFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java index 1b4a43baa6b..7f10492234d 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterFunctionTest.java @@ -16,14 +16,14 @@ public class DataCenterFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DataCenterFunction target = new DataCenterFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java index b639c0f2f59..f5ce0d12a74 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DataCenterInFunctionTest.java @@ -20,14 +20,14 @@ public class DataCenterInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DataCenterInFunction target = new DataCenterInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'datacenters' is required and has to be an array of strings"); } @@ -35,7 +35,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'datacenters' is required and has to be an array of strings"); } @@ -43,7 +43,7 @@ public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("datacenters", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("datacenters", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -54,10 +54,10 @@ public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArrayOfStrings() { // given - final ArrayNode datacentersNode = mapper.createArrayNode(); + final ArrayNode datacentersNode = MAPPER.createArrayNode(); datacentersNode.add(TextNode.valueOf("test")); datacentersNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("datacenters", datacentersNode); + final ObjectNode config = MAPPER.createObjectNode().set("datacenters", datacentersNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -101,8 +101,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithDataCenters(String... dataCenters) { - final ArrayNode dataCentersNode = mapper.createArrayNode(); + final ArrayNode dataCentersNode = MAPPER.createArrayNode(); Arrays.stream(dataCenters).map(TextNode::valueOf).forEach(dataCentersNode::add); - return mapper.createObjectNode().set("datacenters", dataCentersNode); + return MAPPER.createObjectNode().set("datacenters", dataCentersNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java index ef5508cd7f7..635d4701308 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryFunctionTest.java @@ -18,14 +18,14 @@ public class DeviceCountryFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DeviceCountryFunction target = new DeviceCountryFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java index 491f3e11c34..ef07231b835 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceCountryInFunctionTest.java @@ -22,14 +22,14 @@ public class DeviceCountryInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DeviceCountryInFunction target = new DeviceCountryInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'countries' is required and has to be an array of strings"); } @@ -37,7 +37,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'countries' is required and has to be an array of strings"); } @@ -45,7 +45,7 @@ public void validateConfigShouldThrowErrorWhenDatacentersFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("countries", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("countries", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -56,10 +56,10 @@ public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenDatacentersFieldIsNotAnArrayOfStrings() { // given - final ArrayNode countriesNode = mapper.createArrayNode(); + final ArrayNode countriesNode = MAPPER.createArrayNode(); countriesNode.add(TextNode.valueOf("test")); countriesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("countries", countriesNode); + final ObjectNode config = MAPPER.createObjectNode().set("countries", countriesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -104,8 +104,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithCountries(String... countries) { - final ArrayNode countriesNode = mapper.createArrayNode(); + final ArrayNode countriesNode = MAPPER.createArrayNode(); Arrays.stream(countries).map(TextNode::valueOf).forEach(countriesNode::add); - return mapper.createObjectNode().set("countries", countriesNode); + return MAPPER.createObjectNode().set("countries", countriesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java index d7f8849d4d0..9e4fe40ec75 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeFunctionTest.java @@ -17,14 +17,14 @@ public class DeviceTypeFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DeviceTypeFunction target = new DeviceTypeFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java index 1c04d102eda..0bbfc96d6ca 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DeviceTypeInFunctionTest.java @@ -21,14 +21,14 @@ public class DeviceTypeInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DeviceTypeInFunction target = new DeviceTypeInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'types' is required and has to be an array of integers"); } @@ -36,7 +36,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'types' is required and has to be an array of integers"); } @@ -44,7 +44,7 @@ public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("types", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("types", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -55,10 +55,10 @@ public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArrayOfIntegers() { // given - final ArrayNode typesNode = mapper.createArrayNode(); + final ArrayNode typesNode = MAPPER.createArrayNode(); typesNode.add(TextNode.valueOf("test")); typesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("types", typesNode); + final ObjectNode config = MAPPER.createObjectNode().set("types", typesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -103,8 +103,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithTypes(int... types) { - final ArrayNode typesNode = mapper.createArrayNode(); + final ArrayNode typesNode = MAPPER.createArrayNode(); Arrays.stream(types).mapToObj(IntNode::valueOf).forEach(typesNode::add); - return mapper.createObjectNode().set("types", typesNode); + return MAPPER.createObjectNode().set("types", typesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java index d1f41058a97..8183ce51dc2 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainFunctionTest.java @@ -20,14 +20,14 @@ public class DomainFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DomainFunction target = new DomainFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java index 08946ea26cc..20be78ab820 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/DomainInFunctionTest.java @@ -24,14 +24,14 @@ public class DomainInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final DomainInFunction target = new DomainInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'domains' is required and has to be an array of strings"); } @@ -39,7 +39,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'domains' is required and has to be an array of strings"); } @@ -47,7 +47,7 @@ public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("domains", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("domains", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -58,10 +58,10 @@ public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArrayOfStrings() { // given - final ArrayNode domainsNode = mapper.createArrayNode(); + final ArrayNode domainsNode = MAPPER.createArrayNode(); domainsNode.add(TextNode.valueOf("test")); domainsNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("domains", domainsNode); + final ObjectNode config = MAPPER.createObjectNode().set("domains", domainsNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -234,8 +234,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithDomains(String... domains) { - final ArrayNode domainsNode = mapper.createArrayNode(); + final ArrayNode domainsNode = MAPPER.createArrayNode(); Arrays.stream(domains).map(TextNode::valueOf).forEach(domainsNode::add); - return mapper.createObjectNode().set("domains", domainsNode); + return MAPPER.createObjectNode().set("domains", domainsNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java index c8fb959133a..c720f57ff0a 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidAvailableFunctionTest.java @@ -20,14 +20,14 @@ public class EidAvailableFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final EidAvailableFunction target = new EidAvailableFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java index 42a0e75e59b..f0018bc18f1 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/EidInFunctionTest.java @@ -23,14 +23,14 @@ public class EidInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final EidInFunction target = new EidInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'sources' is required and has to be an array of strings"); } @@ -38,7 +38,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'sources' is required and has to be an array of strings"); } @@ -46,7 +46,7 @@ public void validateConfigShouldThrowErrorWhenDomainsFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("sources", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("sources", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -57,10 +57,10 @@ public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenDomainsFieldIsNotAnArrayOfStrings() { // given - final ArrayNode sourcesNode = mapper.createArrayNode(); + final ArrayNode sourcesNode = MAPPER.createArrayNode(); sourcesNode.add(TextNode.valueOf("test")); sourcesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("sources", sourcesNode); + final ObjectNode config = MAPPER.createObjectNode().set("sources", sourcesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -107,8 +107,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithSources(String... sources) { - final ArrayNode sourcesNode = mapper.createArrayNode(); + final ArrayNode sourcesNode = MAPPER.createArrayNode(); Arrays.stream(sources).map(TextNode::valueOf).forEach(sourcesNode::add); - return mapper.createObjectNode().set("sources", sourcesNode); + return MAPPER.createObjectNode().set("sources", sourcesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java index 0c7febd95e9..dff830c0e2b 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/FpdAvailableFunctionTest.java @@ -26,14 +26,14 @@ public class FpdAvailableFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final FpdAvailableFunction target = new FpdAvailableFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -57,7 +57,7 @@ public void extractShouldReturnTrueWhenUserDataPresent() { @Test public void extractShouldReturnTrueWhenUserExtDataPresent() { // given - final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final ObjectNode extData = MAPPER.createObjectNode().set("someData", TextNode.valueOf("someData")); final BidRequest bidRequest = BidRequest.builder() .user(User.builder().ext(ExtUser.builder().data(extData).build()).build()) .build(); @@ -88,7 +88,7 @@ public void extractShouldReturnTrueWhenSiteContentDataPresent() { @Test public void extractShouldReturnTrueWhenSiteExtDataPresent() { // given - final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final ObjectNode extData = MAPPER.createObjectNode().set("someData", TextNode.valueOf("someData")); final Site site = Site.builder() .ext(ExtSite.of(null, extData)) .build(); @@ -123,7 +123,7 @@ public void extractShouldReturnTrueWhenAppContentDataPresent() { @Test public void extractShouldReturnTrueWhenAppExtDataPresent() { // given - final ObjectNode extData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final ObjectNode extData = MAPPER.createObjectNode().set("someData", TextNode.valueOf("someData")); final App app = App.builder() .ext(ExtApp.of(null, extData)) .build(); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java index 532dcb081a3..6cff68fbe1d 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidAvailableFunctionTest.java @@ -21,14 +21,14 @@ public class GppSidAvailableFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final GppSidAvailableFunction target = new GppSidAvailableFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java index 8c9470b52ca..25bbcd00b1d 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/GppSidInFunctionTest.java @@ -6,8 +6,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.iab.openrtb.request.BidRequest; -import com.iab.openrtb.request.Imp; -import com.iab.openrtb.request.Native; import com.iab.openrtb.request.Regs; import org.junit.jupiter.api.Test; import org.prebid.server.auction.model.AuctionContext; @@ -24,14 +22,14 @@ class GppSidInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final GppSidInFunction target = new GppSidInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'sids' is required and has to be an array of integers"); } @@ -39,7 +37,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenSidsFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'sids' is required and has to be an array of integers"); } @@ -47,7 +45,7 @@ public void validateConfigShouldThrowErrorWhenSidsFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenSidsFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("sids", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("sids", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -58,10 +56,10 @@ public void validateConfigShouldThrowErrorWhenSidsFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenSidsFieldIsNotAnArrayOfStrings() { // given - final ArrayNode sidsNode = mapper.createArrayNode(); + final ArrayNode sidsNode = MAPPER.createArrayNode(); sidsNode.add(TextNode.valueOf("test")); sidsNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("sids", sidsNode); + final ObjectNode config = MAPPER.createObjectNode().set("sids", sidsNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -111,8 +109,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithSids(int... sids) { - final ArrayNode sidsNode = mapper.createArrayNode(); + final ArrayNode sidsNode = MAPPER.createArrayNode(); Arrays.stream(sids).mapToObj(IntNode::valueOf).forEach(sidsNode::add); - return mapper.createObjectNode().set("sids", sidsNode); + return MAPPER.createObjectNode().set("sids", sidsNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java index b6a4bcd8e7e..50482b7e042 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunctionTest.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; -import com.iab.openrtb.request.App; import com.iab.openrtb.request.Audio; import com.iab.openrtb.request.Banner; import com.iab.openrtb.request.BidRequest; @@ -27,14 +26,14 @@ public class MediaTypeInFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final MediaTypeInFunction target = new MediaTypeInFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'types' is required and has to be an array of strings"); } @@ -42,7 +41,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'types' is required and has to be an array of strings"); } @@ -50,7 +49,7 @@ public void validateConfigShouldThrowErrorWhenTypesFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { // given - final ObjectNode config = mapper.createObjectNode().set("types", TextNode.valueOf("test")); + final ObjectNode config = MAPPER.createObjectNode().set("types", TextNode.valueOf("test")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -61,10 +60,10 @@ public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArray() { @Test public void validateConfigShouldThrowErrorWhenTypesFieldIsNotAnArrayOfStrings() { // given - final ArrayNode typesNode = mapper.createArrayNode(); + final ArrayNode typesNode = MAPPER.createArrayNode(); typesNode.add(TextNode.valueOf("test")); typesNode.add(IntNode.valueOf(1)); - final ObjectNode config = mapper.createObjectNode().set("types", typesNode); + final ObjectNode config = MAPPER.createObjectNode().set("types", typesNode); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -179,8 +178,8 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithTypes(String... types) { - final ArrayNode typesNode = mapper.createArrayNode(); + final ArrayNode typesNode = MAPPER.createArrayNode(); Arrays.stream(types).map(TextNode::valueOf).forEach(typesNode::add); - return mapper.createObjectNode().set("types", typesNode); + return MAPPER.createObjectNode().set("types", typesNode); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java index 2467191481c..7a8f5f4e2c4 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/PrebidKeyFunctionTest.java @@ -19,14 +19,14 @@ public class PrebidKeyFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final PrebidKeyFunction target = new PrebidKeyFunction(); @Test public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'key' is required and has to be a string"); } @@ -34,7 +34,7 @@ public void validateConfigShouldThrowErrorWhenConfigIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenKeyFieldIsAbsent() { // when and then - assertThatThrownBy(() -> target.validateConfig(mapper.createObjectNode())) + assertThatThrownBy(() -> target.validateConfig(MAPPER.createObjectNode())) .isInstanceOf(ConfigurationValidationException.class) .hasMessage("Field 'key' is required and has to be a string"); } @@ -42,7 +42,7 @@ public void validateConfigShouldThrowErrorWhenKeyFieldIsAbsent() { @Test public void validateConfigShouldThrowErrorWhenKeyFieldIsNotAString() { // given - final ObjectNode config = mapper.createObjectNode().set("key", IntNode.valueOf(1)); + final ObjectNode config = MAPPER.createObjectNode().set("key", IntNode.valueOf(1)); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -53,7 +53,7 @@ public void validateConfigShouldThrowErrorWhenKeyFieldIsNotAString() { @Test public void extractShouldReturnExtPrebidKvpsValueBySpecifiedKey() { // given - final ObjectNode extPrebidKvpsNode = mapper.createObjectNode().set("key", TextNode.valueOf("value")); + final ObjectNode extPrebidKvpsNode = MAPPER.createObjectNode().set("key", TextNode.valueOf("value")); final BidRequest bidRequest = BidRequest.builder() .ext(ExtRequest.of(ExtRequestPrebid.builder().kvps(extPrebidKvpsNode).build())) .build(); @@ -88,6 +88,6 @@ private SchemaFunctionArguments givenFunctionArg } private ObjectNode givenConfigWithKey(String key) { - return mapper.createObjectNode().set("key", TextNode.valueOf(key)); + return MAPPER.createObjectNode().set("key", TextNode.valueOf(key)); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java index 0b33bb9e1bd..1fd079243f5 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/TcfInScopeFunctionTest.java @@ -17,14 +17,14 @@ public class TcfInScopeFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final TcfInScopeFunction target = new TcfInScopeFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java index 5e0ec43abb3..c834345f88c 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/UserFpdAvailableFunctionTest.java @@ -21,14 +21,14 @@ public class UserFpdAvailableFunctionTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private final UserFpdAvailableFunction target = new UserFpdAvailableFunction(); @Test public void validateConfigShouldThrowErrorWhenArgumentsArePresent() { // given - final ObjectNode config = mapper.createObjectNode().set("args", TextNode.valueOf("args")); + final ObjectNode config = MAPPER.createObjectNode().set("args", TextNode.valueOf("args")); // when and then assertThatThrownBy(() -> target.validateConfig(config)) @@ -52,7 +52,7 @@ public void extractShouldReturnTrueWhenNonNullUserDataIsPresent() { @Test public void extractShouldReturnTrueWhenUserExtDataIsPresent() { // given - final ObjectNode extUserData = mapper.createObjectNode().set("someData", TextNode.valueOf("someData")); + final ObjectNode extUserData = MAPPER.createObjectNode().set("someData", TextNode.valueOf("someData")); final BidRequest bidRequest = BidRequest.builder() .user(User.builder().ext(ExtUser.builder().data(extUserData).build()).build()) .build(); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java index e4cd9791026..a53fd44a31b 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/CompositeRuleTest.java @@ -51,7 +51,7 @@ public void processShouldAccumulateResultFromAllSubrules() { asList(ActivityImpl.of("firstActivity", "success", emptyList()), ActivityImpl.of("secondActivity", "success", emptyList()))); - List expectedNonBids = List.of( + final List expectedNonBids = List.of( SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID))), SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java index 967b637260a..5b1bd6186d4 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRuleTest.java @@ -37,7 +37,7 @@ @ExtendWith(MockitoExtension.class) public class ConditionalRuleTest { - private final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private static final String ANALYTICS_KEY = "analyticsKey"; private static final String MODEL_VERSION = "modelVersion"; @@ -71,8 +71,8 @@ public void processShouldCorrectlyProcessData() { final Object context = new Object(); // two schema functions - final ObjectNode firstSchemaFunctionConfig = mapper.createObjectNode(); - final ObjectNode secondSchemaFunctionConfig = mapper.createObjectNode(); + final ObjectNode firstSchemaFunctionConfig = MAPPER.createObjectNode(); + final ObjectNode secondSchemaFunctionConfig = MAPPER.createObjectNode(); final String firstSchemaFunctionName = "firstFunction"; final String secondSchemaFunctionName = "secondFunction"; final String firstSchemaFunctionOutput = "firstSchemaOutput"; @@ -90,8 +90,8 @@ public void processShouldCorrectlyProcessData() { // two result functions final String firstRuleActionName = "firstRuleAction"; final String secondRuleActionName = "secondRuleAction"; - final ObjectNode firstResultFunctionConfig = mapper.createObjectNode(); - final ObjectNode secondResultFunctionConfig = mapper.createObjectNode(); + final ObjectNode firstResultFunctionConfig = MAPPER.createObjectNode(); + final ObjectNode secondResultFunctionConfig = MAPPER.createObjectNode(); final List> resultFunctionHolders = List.of( ResultFunctionHolder.of(firstRuleActionName, firstResultFunction, firstResultFunctionConfig), ResultFunctionHolder.of(secondRuleActionName, secondResultFunction, secondResultFunctionConfig)); diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java index d650a1a881f..2a9fa1ceec4 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/rules/DefaultActionRuleTest.java @@ -26,15 +26,15 @@ public class DefaultActionRuleTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); @Test public void processShouldAccumulateResultFromAllRuleActions() { // given - final Object VALUE = new Object(); - final Object CONTEXT = new Object(); + final Object value = new Object(); + final Object context = new Object(); - final ObjectNode firstConfig = mapper.createObjectNode().set("config", TextNode.valueOf("test")); + final ObjectNode firstConfig = MAPPER.createObjectNode().set("config", TextNode.valueOf("test")); final ResultFunction firstFunction = (ResultFunction) mock(ResultFunction.class); given(firstFunction.apply(any())).willAnswer(invocationOnMock -> RuleResult.of( @@ -43,7 +43,7 @@ public void processShouldAccumulateResultFromAllRuleActions() { TagsImpl.of(singletonList(ActivityImpl.of("firstActivity", "success", emptyList()))), singletonList(SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID)))))); - final ObjectNode secondConfig = mapper.createObjectNode().set("config", TextNode.valueOf("anotherTest")); + final ObjectNode secondConfig = MAPPER.createObjectNode().set("config", TextNode.valueOf("anotherTest")); final ResultFunction secondFunction = (ResultFunction) mock(ResultFunction.class); given(secondFunction.apply(any())).willAnswer(invocationOnMock -> RuleResult.of( @@ -60,17 +60,17 @@ public void processShouldAccumulateResultFromAllRuleActions() { actions, "analyticsKey", "modelVersion"); // when - final RuleResult result = target.process(VALUE, CONTEXT); + final RuleResult result = target.process(value, context); // then final Tags expectedTags = TagsImpl.of( asList(ActivityImpl.of("firstActivity", "success", emptyList()), ActivityImpl.of("secondActivity", "success", emptyList()))); - List expectedNonBids = List.of( + final List expectedNonBids = List.of( SeatNonBid.of("firstSeat", singletonList(NonBid.of("1", BidRejectionReason.NO_BID))), SeatNonBid.of("secondSeat", singletonList(NonBid.of("2", BidRejectionReason.NO_BID)))); - assertThat(result).isEqualTo(RuleResult.of(VALUE, RuleAction.UPDATE, expectedTags, expectedNonBids)); + assertThat(result).isEqualTo(RuleResult.of(value, RuleAction.UPDATE, expectedTags, expectedNonBids)); } } diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java index 0801917c7ad..6b4dc56b70b 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java @@ -35,7 +35,7 @@ @ExtendWith(MockitoExtension.class) class PbRuleEngineProcessedAuctionRequestHookTest { - private static final ObjectMapper mapper = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); private PbRuleEngineProcessedAuctionRequestHook target; @@ -87,7 +87,7 @@ public void callShouldReturnNoActionWhenNoAccountConfigProvided() { @Test public void callShouldReturnNoActionWhenRuleActionIsNoAction() { // given - given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(invocationContext.accountConfig()).willReturn(MAPPER.createObjectNode()); given(processedAuctionRequestRule.process( bidRequest, RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) @@ -104,7 +104,7 @@ public void callShouldReturnNoActionWhenRuleActionIsNoAction() { @Test public void callShouldReturnPayloadUpdateWhenRuleActionIsUpdate() { // given - given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(invocationContext.accountConfig()).willReturn(MAPPER.createObjectNode()); given(processedAuctionRequestRule.process( bidRequest, RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) @@ -122,7 +122,7 @@ public void callShouldReturnPayloadUpdateWhenRuleActionIsUpdate() { @Test public void callShouldReturnRejectWhenRuleActionIsReject() { // given - given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(invocationContext.accountConfig()).willReturn(MAPPER.createObjectNode()); given(processedAuctionRequestRule.process( bidRequest, RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) @@ -140,7 +140,7 @@ public void callShouldReturnRejectWhenRuleActionIsReject() { @Test public void callShouldReturnFailureOnFailure() { // given - given(invocationContext.accountConfig()).willReturn(mapper.createObjectNode()); + given(invocationContext.accountConfig()).willReturn(MAPPER.createObjectNode()); given(processedAuctionRequestRule.process( bidRequest, RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) From 28c472f2fc9b188eac71c24d330726e56d3ca7f4 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 1 Sep 2025 13:52:42 +0300 Subject: [PATCH 77/94] Made conditional rule throw NoMatchingException when no condition matched. --- .../modules/rule/engine/core/rules/ConditionalRule.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java index 6bd5de9ef03..fae07e3892a 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java @@ -47,13 +47,7 @@ public RuleResult process(T value, C context) { .map(matcher -> StringUtils.defaultIfEmpty(matcher, SchemaFunction.UNDEFINED_RESULT)) .toList(); - final LookupResult> lookupResult; - try { - lookupResult = ruleTree.lookup(matchers); - } catch (NoMatchingRuleException e) { - return RuleResult.noAction(value); - } - + final LookupResult> lookupResult = ruleTree.lookup(matchers); final RuleConfig ruleConfig = lookupResult.getValue(); final InfrastructureArguments infrastructureArguments = From 06c27ae57182b04f70b484c093c6a06c0c6f2b60 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Sep 2025 14:43:09 +0300 Subject: [PATCH 78/94] Fixes after review. --- .../rule/engine/core/config/model/AccountConfig.java | 6 +++--- .../rule/engine/core/config/model/ModelGroupConfig.java | 2 +- .../rule/engine/core/config/model/RuleSetConfig.java | 2 +- .../result/functions/filter/FilterBiddersFunction.java | 4 +--- .../functions/filter/FilterBiddersFunctionConfig.java | 2 +- .../result/functions/filter/FilterBiddersResult.java | 4 ---- .../request/result/functions/log/AnalyticsMapper.java | 4 ++-- .../rule/engine/core/rules/RandomWeightedRule.java | 2 +- .../engine/core/rules/result/InfrastructureArguments.java | 2 +- .../modules/rule/engine/core/util/WeightedEntry.java | 2 +- .../hooks/modules/rule/engine/core/util/WeightedList.java | 8 ++------ 11 files changed, 14 insertions(+), 24 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java index 552da0ac346..fab0cd9f208 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/AccountConfig.java @@ -6,12 +6,12 @@ import lombok.extern.jackson.Jacksonized; import java.time.Instant; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +@Value @Builder @Jacksonized -@Value(staticConstructor = "of") public class AccountConfig { @Builder.Default @@ -22,5 +22,5 @@ public class AccountConfig { @Builder.Default @JsonProperty("ruleSets") - List ruleSets = new ArrayList<>(); + List ruleSets = Collections.emptyList(); } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java index b1fa3d0c2b9..f646de8985e 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ModelGroupConfig.java @@ -6,8 +6,8 @@ import java.util.List; +@Value @Builder -@Value(staticConstructor = "of") public class ModelGroupConfig { int weight; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java index 06648313f9d..9c54f100977 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/RuleSetConfig.java @@ -8,9 +8,9 @@ import java.util.List; +@Value @Builder @Jacksonized -@Value(staticConstructor = "of") public class RuleSetConfig { @Builder.Default diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index 84886c4f677..8e2887c4ed8 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -55,9 +55,7 @@ public RuleResult apply(ResultFunctionArguments seatNonBid = new ArrayList<>(); for (Imp imp : bidRequest.getImp()) { - if (granularity instanceof Granularity.Imp - && !StringUtils.equals(((Granularity.Imp) granularity).impId(), imp.getId())) { - + if (granularity instanceof Granularity.Imp(String impId) && !StringUtils.equals(impId, imp.getId())) { updatedImps.add(imp); continue; } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index 780943fe996..a75fdeb99c1 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -8,9 +8,9 @@ import java.util.Set; +@Value @Builder @Jacksonized -@Value(staticConstructor = "of") public class FilterBiddersFunctionConfig { Set bidders; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java index 64e2a227050..48f71682f84 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersResult.java @@ -1,13 +1,11 @@ package org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter; import com.iab.openrtb.request.Imp; -import lombok.experimental.Accessors; import java.util.Set; public sealed interface FilterBiddersResult { - @Accessors(fluent = true) record NoAction() implements FilterBiddersResult { private static final NoAction INSTANCE = new NoAction(); @@ -16,11 +14,9 @@ public static NoAction instance() { } } - @Accessors(fluent = true) record Update(Imp imp, Set bidders) implements FilterBiddersResult { } - @Accessors(fluent = true) record Reject(Set bidders) implements FilterBiddersResult { } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java index 74a181cd3a9..68c78e65d53 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/log/AnalyticsMapper.java @@ -41,8 +41,8 @@ public static Tags toTags(ObjectMapper mapper, LogATagFunction.NAME); final Granularity granularity = infrastructureArguments.getContext().getGranularity(); - final List impIds = granularity instanceof Granularity.Imp - ? Collections.singletonList(((Granularity.Imp) granularity).impId()) + final List impIds = granularity instanceof Granularity.Imp(String impId) + ? Collections.singletonList(impId) : Collections.singletonList("*"); final Result result = ResultImpl.of( diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java index 5ecdfc580f7..e3275ef0621 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/RandomWeightedRule.java @@ -13,6 +13,6 @@ public class RandomWeightedRule implements Rule { @Override public RuleResult process(T value, C context) { - return weightedList.getForSeed(random.nextInt(weightedList.maxSeed() + 1)).process(value, context); + return weightedList.getForSeed(random.nextInt(weightedList.maxSeed())).process(value, context); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java index 3a9f2e5ee26..f30eb769ddf 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/result/InfrastructureArguments.java @@ -5,8 +5,8 @@ import java.util.Map; +@Value @Builder -@Value(staticConstructor = "of") public class InfrastructureArguments { C context; diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java index 7dd53f148d4..977a7a6daab 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedEntry.java @@ -13,7 +13,7 @@ private WeightedEntry(int weight, T value) { this.weight = weight; this.value = value; - if (weight < 0) { + if (weight <= 0) { throw new IllegalArgumentException("Weight must be greater than zero"); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java index 7bc3c1cb707..92fcd2e455b 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/util/WeightedList.java @@ -23,10 +23,6 @@ private void validateEntries(List> entries) { if (CollectionUtils.isEmpty(entries)) { throw new IllegalArgumentException("Weighted list cannot be empty"); } - - if (entries.stream().anyMatch(weightedEntry -> weightedEntry.getWeight() <= 0)) { - throw new IllegalArgumentException("Weighted entry should have weight greater than zero"); - } } private List> prepareEntries(List> entries) { @@ -43,12 +39,12 @@ private List> prepareEntries(List> entries) { } public T getForSeed(int seed) { - if (seed < 0 || seed > maxSeed()) { + if (seed < 0 || seed >= maxSeed()) { throw new IllegalArgumentException("Seed number must be between 0 and " + weightSum); } for (WeightedEntry entry : entries) { - if (seed <= entry.getWeight()) { + if (seed < entry.getWeight()) { return entry.getValue(); } } From ef511de48073d181e7b87085923121ed0faf16d2 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Sep 2025 16:39:55 +0300 Subject: [PATCH 79/94] Bumped module version. --- extra/modules/pb-rule-engine/pom.xml | 2 +- .../modules/rule/engine/core/config/StageConfigParser.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/extra/modules/pb-rule-engine/pom.xml b/extra/modules/pb-rule-engine/pom.xml index 773e6b1a2c8..df22f4c5d4b 100644 --- a/extra/modules/pb-rule-engine/pom.xml +++ b/extra/modules/pb-rule-engine/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.31.0-SNAPSHOT + 3.32.0-SNAPSHOT pb-rule-engine diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index ec592e76057..baad5a35b09 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -9,8 +9,8 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; @@ -161,9 +161,7 @@ private void validateActionConfig(ResultFunctionHolder action) { } } - private Rule combineRules( - Rule left, Rule right) { - + private Rule combineRules(Rule left, Rule right) { if (left == null && right == null) { return NoOpRule.create(); } else if (left != null && right != null) { From 724eadc34293698b9363ad303ab6618072c312eb Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Wed, 3 Sep 2025 16:44:10 +0300 Subject: [PATCH 80/94] Fixed styling. --- .../hooks/modules/rule/engine/core/rules/ConditionalRule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java index fae07e3892a..c56c580d149 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/ConditionalRule.java @@ -1,7 +1,6 @@ package org.prebid.server.hooks.modules.rule.engine.core.rules; import org.apache.commons.lang3.StringUtils; -import org.prebid.server.hooks.modules.rule.engine.core.rules.exception.NoMatchingRuleException; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.InfrastructureArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionArguments; import org.prebid.server.hooks.modules.rule.engine.core.rules.result.ResultFunctionHolder; From 31ae5493fea1e4f3dc3ddb23f22dd9305f609480 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 19 Sep 2025 17:29:23 +0300 Subject: [PATCH 81/94] Added checks for ambiguity. --- .../core/rules/tree/RuleTreeFactory.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java index 1e378ef74fe..83acf0455c6 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/tree/RuleTreeFactory.java @@ -17,13 +17,33 @@ public static RuleTree> buildTree(List> final List>> parsingContexts = toParsingContexts(rules); final int depth = getDepth(parsingContexts); + if (depth == 0) { + throw new InvalidMatcherConfiguration("Rule with no matchers"); + } + if (!parsingContexts.stream().allMatch(context -> context.argumentMatchers().size() == depth)) { throw new InvalidMatcherConfiguration("Mismatched arguments count"); } + validateRules(parsingContexts); + return new RuleTree<>(parseRuleNode(parsingContexts), depth); } + private static void validateRules(List>> parsingContexts) { + final List ambiguousRules = parsingContexts.stream() + .collect(Collectors.groupingBy(context -> context.value().getCondition(), Collectors.counting())) + .entrySet() + .stream() + .filter(entry -> entry.getValue() > 1) + .map(Map.Entry::getKey) + .toList(); + + if (!ambiguousRules.isEmpty()) { + throw new InvalidMatcherConfiguration("Ambiguous matchers: " + String.join(", ", ambiguousRules)); + } + } + private static List>> toParsingContexts(List> rules) { return rules.stream() .map(rule -> new ParsingContext<>( From 7e07e46877ea1b7b86782ee1758e902dbf752498 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 22 Sep 2025 16:22:56 +0300 Subject: [PATCH 82/94] Updates after merging master. --- extra/modules/pb-rule-engine/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/modules/pb-rule-engine/pom.xml b/extra/modules/pb-rule-engine/pom.xml index df22f4c5d4b..1903ee73045 100644 --- a/extra/modules/pb-rule-engine/pom.xml +++ b/extra/modules/pb-rule-engine/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.32.0-SNAPSHOT + 3.33.0-SNAPSHOT pb-rule-engine From 00dabfe23bcf1b0bdf00f879b6ae26ad53f2326b Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 22 Sep 2025 16:57:47 +0300 Subject: [PATCH 83/94] Added support for rejection tracking. --- ...RuleEngineProcessedAuctionRequestHook.java | 34 ++++++++++++++----- ...EngineProcessedAuctionRequestHookTest.java | 16 ++++++++- .../auction/model/BidRejectionReason.java | 2 -- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index 5199ed15b48..ed1bc8c2599 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -5,6 +5,8 @@ import io.vertx.core.Future; import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.ImpRejection; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.hooks.execution.v1.InvocationResultImpl; import org.prebid.server.hooks.execution.v1.auction.AuctionRequestPayloadImpl; import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; @@ -19,8 +21,12 @@ import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; import org.prebid.server.hooks.v1.auction.ProcessedAuctionRequestHook; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; public class PbRuleEngineProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -60,6 +66,7 @@ private static Future> succeeded(RuleRes InvocationResultImpl.builder() .status(InvocationStatus.success) .action(toInvocationAction(result.getAction())) + .rejections(toRejections(result.getSeatNonBid())) .analyticsTags(result.getAnalyticsTags()); if (result.isUpdate()) { @@ -69,6 +76,25 @@ private static Future> succeeded(RuleRes return Future.succeededFuture(resultBuilder.build()); } + private static InvocationAction toInvocationAction(RuleAction ruleAction) { + return switch (ruleAction) { + case NO_ACTION -> InvocationAction.no_action; + case UPDATE -> InvocationAction.update; + case REJECT -> InvocationAction.reject; + }; + } + + private static List toRejections(SeatNonBid seatNonBid) { + return seatNonBid.getNonBid().stream() + .map(nonBid -> (Rejection) ImpRejection.of(nonBid.getImpId(), nonBid.getStatusCode())) + .toList(); + } + + private static Map> toRejections(List seatNonBids) { + return seatNonBids.stream() + .collect(Collectors.toMap(SeatNonBid::getSeat, PbRuleEngineProcessedAuctionRequestHook::toRejections)); + } + private static Future> failure(Throwable error) { return Future.succeededFuture( InvocationResultImpl.builder() @@ -78,14 +104,6 @@ private static Future> failure(Throwable .build()); } - private static InvocationAction toInvocationAction(RuleAction ruleAction) { - return switch (ruleAction) { - case NO_ACTION -> InvocationAction.no_action; - case UPDATE -> InvocationAction.update; - case REJECT -> InvocationAction.reject; - }; - } - @Override public String code() { return CODE; diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java index 6b4dc56b70b..97e7d19e18b 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHookTest.java @@ -9,6 +9,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.auction.model.AuctionContext; +import org.prebid.server.auction.model.BidRejectionReason; +import org.prebid.server.auction.model.ImpRejection; +import org.prebid.server.auction.model.Rejection; import org.prebid.server.exception.PreBidException; import org.prebid.server.hooks.modules.rule.engine.core.config.RuleParser; import org.prebid.server.hooks.modules.rule.engine.core.request.Granularity; @@ -22,10 +25,14 @@ import org.prebid.server.hooks.v1.analytics.Tags; import org.prebid.server.hooks.v1.auction.AuctionInvocationContext; import org.prebid.server.hooks.v1.auction.AuctionRequestPayload; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.NonBid; +import org.prebid.server.proto.openrtb.ext.response.seatnonbid.SeatNonBid; import org.prebid.server.settings.model.Account; import java.time.Instant; import java.util.Collections; +import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -104,16 +111,23 @@ public void callShouldReturnNoActionWhenRuleActionIsNoAction() { @Test public void callShouldReturnPayloadUpdateWhenRuleActionIsUpdate() { // given + final SeatNonBid seatNonBid = SeatNonBid.of( + "bidder", Collections.singletonList(NonBid.of("impId", BidRejectionReason.NO_BID))); + given(invocationContext.accountConfig()).willReturn(MAPPER.createObjectNode()); given(processedAuctionRequestRule.process( bidRequest, RequestRuleContext.of(auctionContext, Granularity.Request.instance(), "datacenter"))) - .willReturn(RuleResult.of(bidRequest, RuleAction.UPDATE, tags, Collections.emptyList())); + .willReturn(RuleResult.of(bidRequest, RuleAction.UPDATE, tags, Collections.singletonList(seatNonBid))); // when and then + final Map> rejections = Map.of( + "bidder", List.of(ImpRejection.of("impId", BidRejectionReason.NO_BID))); + assertThat(target.call(payload, invocationContext).result()).satisfies(invocationResult -> { assertThat(invocationResult.status()).isEqualTo(InvocationStatus.success); assertThat(invocationResult.action()).isEqualTo(InvocationAction.update); + assertThat(invocationResult.rejections()).containsExactlyEntriesOf(rejections); assertThat(invocationResult.payloadUpdate()).isNotNull(); assertThat(invocationResult.analyticsTags()).isEqualTo(tags); }); diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java index 0d34c77a5fe..e44f3e78150 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionReason.java @@ -56,8 +56,6 @@ public enum BidRejectionReason { */ REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE(202), - REQUEST_BLOCKED_OPTIMIZED(203), - /** * This impression not sent to the bid adapter because the impression or the bidder was removed from the request. */ From 2cbeb019691fa6e82f270a764631fa37c64c8da6 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 22 Sep 2025 17:04:20 +0300 Subject: [PATCH 84/94] Removed unnecessary @Accessors annotation. --- .../hooks/modules/rule/engine/core/config/RuleParser.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java index c027ee214a3..b4d816c3c6d 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java @@ -5,7 +5,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import io.vertx.core.Future; import io.vertx.core.Vertx; -import lombok.experimental.Accessors; import org.prebid.server.exception.PreBidException; import org.prebid.server.execution.retry.RetryPolicy; import org.prebid.server.execution.retry.Retryable; @@ -140,11 +139,9 @@ private sealed interface ParsingAttempt { RetryPolicy retryPolicy(); - @Accessors(fluent = true) record Failed(Instant timestamp, RetryPolicy retryPolicy) implements ParsingAttempt { } - @Accessors(fluent = true) record InProgress(Instant timestamp, RetryPolicy retryPolicy) implements ParsingAttempt { public Failed failed() { From bbd7a9b6914593ab142fb6a39eecab7e78c3ccd2 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 29 Sep 2025 15:38:11 +0300 Subject: [PATCH 85/94] Minor fixes. --- .../rule/engine/core/config/StageConfigParser.java | 4 ++-- .../functions/filter/FilterBiddersFunctionConfig.java | 1 + .../engine/core/config/StageConfigParserTest.java | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index baad5a35b09..12c2d9a2720 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -167,9 +167,9 @@ private Rule combineRules(Rule left, Rule right) { } else if (left != null && right != null) { return AlternativeActionRule.of(left, right); } else if (left != null) { - return left; + return AlternativeActionRule.of(left, NoOpRule.create()); } - return right; + return AlternativeActionRule.of(right, NoOpRule.create()); } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index a75fdeb99c1..28ba0f5e518 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -19,6 +19,7 @@ public class FilterBiddersFunctionConfig { @JsonProperty("seatnonbid") BidRejectionReason seatNonBid = BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; + @JsonProperty("ifSyncedId") Boolean ifSyncedId; @JsonProperty("analyticsValue") diff --git a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java index 88127ec9d15..79875a16a0e 100644 --- a/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java +++ b/extra/modules/pb-rule-engine/src/test/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParserTest.java @@ -14,8 +14,8 @@ import org.prebid.server.hooks.modules.rule.engine.core.config.model.SchemaFunctionConfig; import org.prebid.server.hooks.modules.rule.engine.core.rules.AlternativeActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.CompositeRule; -import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.ConditionalRuleFactory; +import org.prebid.server.hooks.modules.rule.engine.core.rules.DefaultActionRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.NoOpRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.RandomWeightedRule; import org.prebid.server.hooks.modules.rule.engine.core.rules.Rule; @@ -126,8 +126,9 @@ public void parseShouldCombineModelGroupRulesUnderSameRuleSetIntoRandomWeightedR // when and then final RandomWeightedRule weightedRule = RandomWeightedRule.of( randomGenerator, - new WeightedList<>( - List.of(WeightedEntry.of(1, matchingRule), WeightedEntry.of(2, matchingRule)))); + new WeightedList<>(List.of( + WeightedEntry.of(1, AlternativeActionRule.of(matchingRule, NoOpRule.create())), + WeightedEntry.of(2, AlternativeActionRule.of(matchingRule, NoOpRule.create()))))); assertThat(target.parse(accountConfig)).isEqualTo( CompositeRule.of(Collections.singletonList(weightedRule))); @@ -196,8 +197,10 @@ public void parseShouldBuildRuleTreeAndCreateAppropriateMatchingRule() { final AccountConfig accountConfig = givenAccountConfig(modelGroupConfig); // when and then + final AlternativeActionRule alternativeRule = AlternativeActionRule.of( + matchingRule, NoOpRule.create()); final RandomWeightedRule weightedRule = RandomWeightedRule.of( - randomGenerator, new WeightedList<>(List.of(WeightedEntry.of(1, matchingRule)))); + randomGenerator, new WeightedList<>(List.of(WeightedEntry.of(1, alternativeRule)))); assertThat(target.parse(accountConfig)).isEqualTo( CompositeRule.of(Collections.singletonList(weightedRule))); From cb7aeafc633f7711dddc1188afe872afcf8ae751 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 30 Sep 2025 14:07:16 +0300 Subject: [PATCH 86/94] Minor fixes. --- .../hooks/modules/rule/engine/core/config/RuleParser.java | 6 ++---- .../modules/rule/engine/core/rules/PerStageRule.java | 8 ++++++++ .../v1/PbRuleEngineProcessedAuctionRequestHook.java | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java index b4d816c3c6d..9933eae0862 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java @@ -5,7 +5,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import io.vertx.core.Future; import io.vertx.core.Vertx; -import org.prebid.server.exception.PreBidException; +import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.execution.retry.RetryPolicy; import org.prebid.server.execution.retry.Retryable; import org.prebid.server.hooks.modules.rule.engine.core.rules.PerStageRule; @@ -67,9 +67,7 @@ public Future parseForAccount(String accountId, ObjectNode config) } parseConfig(accountId, config); - return cachedRule == null - ? Future.failedFuture(new PreBidException("Rule for account " + accountId + " is not ready")) - : Future.succeededFuture(cachedRule); + return Future.succeededFuture(ObjectUtils.defaultIfNull(cachedRule, PerStageRule.noOp())); } private Instant getConfigTimestamp(ObjectNode config) { diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java index 4fbb56573c4..c9d3c3383be 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/rules/PerStageRule.java @@ -13,8 +13,16 @@ @Value(staticConstructor = "of") public class PerStageRule { + private static final PerStageRule NO_OP = PerStageRule.builder() + .processedAuctionRequestRule(NoOpRule.create()) + .build(); + Instant timestamp; Rule processedAuctionRequestRule; + + public static PerStageRule noOp() { + return NO_OP; + } } diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java index ed1bc8c2599..2f20d25ffa6 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/v1/PbRuleEngineProcessedAuctionRequestHook.java @@ -92,7 +92,10 @@ private static List toRejections(SeatNonBid seatNonBid) { private static Map> toRejections(List seatNonBids) { return seatNonBids.stream() - .collect(Collectors.toMap(SeatNonBid::getSeat, PbRuleEngineProcessedAuctionRequestHook::toRejections)); + .collect(Collectors.groupingBy(SeatNonBid::getSeat, + Collectors.flatMapping( + seatNonBid -> toRejections(seatNonBid).stream(), + Collectors.toList()))); } private static Future> failure(Throwable error) { From b1f47a7a755aaab44db0450c3c69381cdd3736ab Mon Sep 17 00:00:00 2001 From: Markiyan Mykush <95693607+marki1an@users.noreply.github.com> Date: Sun, 5 Oct 2025 18:28:53 +0300 Subject: [PATCH 87/94] Test: Rule Engine (#4103) Co-authored-by: Alex Maltsev Co-authored-by: osulzhenko --- .../server/functional/model/ModuleName.groovy | 1 + .../config/ModuleHookImplementation.groovy | 1 + .../model/config/PbRulesEngine.groovy | 19 + .../model/config/PbsModulesConfig.groovy | 1 + .../model/config/ResultFunction.groovy | 22 + .../model/config/RuleEngineArguments.groovy | 11 + .../model/config/RuleEngineFunction.groovy | 44 + .../config/RuleEngineFunctionArgs.groovy | 37 + .../config/RuleEngineModelDefault.groovy | 7 + .../config/RuleEngineModelDefaultArgs.groovy | 6 + .../model/config/RuleEngineModelRule.groovy | 17 + .../config/RuleEngineModelRuleResult.groovy | 38 + .../RuleEngineModelRuleResultsArgs.groovy | 31 + .../model/config/RuleEngineModelSchema.groovy | 20 + .../functional/model/config/RuleSet.groovy | 23 + .../model/config/RulesEngineModelGroup.groovy | 29 + .../model/request/auction/Device.groovy | 2 +- .../model/request/auction/DeviceType.groovy | 22 + .../model/request/auction/ImpUnitCode.groovy | 10 + .../model/request/auction/Prebid.groovy | 2 + .../response/auction/AnalyticResult.groovy | 3 +- .../auction/BidRejectionReason.groovy | 1 + .../model/response/auction/ImpResult.groovy | 3 +- .../model/response/auction/ModuleValue.groovy | 16 +- .../response/auction/ResponseAction.groovy | 2 +- .../model/response/auction/SeatNonBid.groovy | 3 +- .../tests/AlternateBidderCodeSpec.groovy | 14 +- .../functional/tests/BidderParamsSpec.groovy | 4 +- .../functional/tests/SeatNonBidSpec.groovy | 18 +- .../tests/module/AbTestingModuleSpec.groovy | 38 +- .../tests/module/ModuleBaseSpec.groovy | 23 +- .../ortb2blocking/Ortb2BlockingSpec.groovy | 2 +- .../pbruleengine/RuleEngineAliasSpec.groovy | 262 ++++ .../pbruleengine/RuleEngineBaseSpec.groovy | 178 +++ .../pbruleengine/RuleEngineContextSpec.groovy | 1134 +++++++++++++++++ .../pbruleengine/RuleEngineCoreSpec.groovy | 870 +++++++++++++ .../pbruleengine/RuleEngineDeviceSpec.groovy | 434 +++++++ .../RuleEngineInfrastructureSpec.groovy | 264 ++++ .../pbruleengine/RuleEnginePrivacySpec.groovy | 909 +++++++++++++ .../pbruleengine/RuleEngineSpecialSpec.groovy | 319 +++++ .../pbruleengine/RuleEngineSyncSpec.groovy | 298 +++++ .../RuleEngineValidationSpec.groovy | 434 +++++++ .../richmedia/RichMediaFilterSpec.groovy | 16 +- .../pricefloors/PriceFloorsRulesSpec.groovy | 4 +- .../functional/tests/privacy/DsaSpec.groovy | 8 +- .../tests/privacy/GdprAuctionSpec.groovy | 2 +- 46 files changed, 5537 insertions(+), 65 deletions(-) create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy diff --git a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy index 2bc06ab7144..9aee1c69c68 100644 --- a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy @@ -8,6 +8,7 @@ enum ModuleName { PB_RESPONSE_CORRECTION ("pb-response-correction"), ORTB2_BLOCKING("ortb2-blocking"), PB_REQUEST_CORRECTION('pb-request-correction'), + PB_RULE_ENGINE('pb-rule-engine') @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy index 247bdea4353..4d9424e1c39 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy @@ -11,6 +11,7 @@ enum ModuleHookImplementation { ORTB2_BLOCKING_BIDDER_REQUEST("ortb2-blocking-bidder-request"), ORTB2_BLOCKING_RAW_BIDDER_RESPONSE("ortb2-blocking-raw-bidder-response"), PB_REQUEST_CORRECTION_PROCESSED_AUCTION_REQUEST("pb-request-correction-processed-auction-request"), + PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST("pb-rule-engine-processed-auction-request") @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy new file mode 100644 index 00000000000..321c548394b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy @@ -0,0 +1,19 @@ +package org.prebid.server.functional.model.config + +import java.time.ZonedDateTime + +class PbRulesEngine { + + Boolean enabled + Boolean generateRulesFromBidderConfig + ZonedDateTime timestamp + List ruleSets + + static PbRulesEngine createRulesEngineWithRule(Boolean enabled = true) { + new PbRulesEngine().tap { + it.enabled = enabled + it.generateRulesFromBidderConfig = false + it.ruleSets = [RuleSet.createRuleSets()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy index 59f640f966c..801178fd4d4 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy @@ -13,4 +13,5 @@ class PbsModulesConfig { Ortb2BlockingConfig ortb2Blocking PbResponseCorrection pbResponseCorrection PbRequestCorrectionConfig pbRequestCorrection + PbRulesEngine pbRuleEngine } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy new file mode 100644 index 00000000000..108e8a00fe0 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum ResultFunction { + + INCLUDE_BIDDERS("includeBidders"), + EXCLUDE_BIDDER("excludeBidders"), + LOG_A_TAG("logAtag") + + String value + + ResultFunction(String value) { + this.value = value + } + + @Override + @JsonValue + String toString() { + return value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy new file mode 100644 index 00000000000..20f5855be7b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.model.bidder.BidderName + +class RuleEngineArguments { + + List bidders + Integer seatNonBid + Boolean ifSyncedId + String analyticsValue +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy new file mode 100644 index 00000000000..b67b4dac362 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -0,0 +1,44 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum RuleEngineFunction { + + DEVICE_COUNTRY("deviceCountry", null), + DEVICE_COUNTRY_IN("deviceCountryIn", "countries"), + DATA_CENTER("dataCenter", null), + DATA_CENTER_IN("dataCenterIn", "datacenters"), + CHANNEL("channel", null), + EID_AVAILABLE("eidAvailable", null), + EID_IN("eidIn", "sources"), + USER_FPD_AVAILABLE("userFpdAvailable", null), + FPD_AVAILABLE("fpdAvailable", null), + GPP_SID_AVAILABLE("gppSidAvailable", null), + GPP_SID_IN("gppSidIn", "sids"), + TCF_IN_SCOPE("tcfInScope", null), + PERCENT("percent", "pct"), + PREBID_KEY("prebidKey", "key"), + DOMAIN("domain", null), + DOMAIN_IN("domainIn", "domains"), + BUNDLE("bundle", null), + BUNDLE_IN("bundleIn", "bundles"), + MEDIA_TYPE_IN("mediaTypeIn", "types"), + AD_UNIT_CODE("adUnitCode", null), + AD_UNIT_CODE_IN("adUnitCodeIn", "codes"), + DEVICE_TYPE("deviceType", null), + DEVICE_TYPE_IN("deviceTypeIn", "types") + + private String value + private String fieldName + + RuleEngineFunction(String value, String fieldName) { + this.value = value + this.fieldName = fieldName + } + + @JsonValue + @Override + String toString() { + return value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy new file mode 100644 index 00000000000..a2cb809d0fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy @@ -0,0 +1,37 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonProperty +import org.prebid.server.functional.util.PBSUtils + +class RuleEngineFunctionArgs { + + List countries + List datacenters + List sources + List sids + @JsonProperty("pct") + Object percent + Object key + List domains + List bundles + List codes + List types + String operator + BigDecimal value + Currency currency + + static RuleEngineFunctionArgs getDefaultFunctionArgs() { + new RuleEngineFunctionArgs().tap { + countries = [PBSUtils.randomString] + datacenters = [PBSUtils.randomString] + sources = [PBSUtils.randomString] + sids = [PBSUtils.randomNumber] + percent = PBSUtils.getRandomNumber(1, 100) + key = PBSUtils.randomString + domains = [PBSUtils.randomString] + bundles = [PBSUtils.randomString] + codes = [PBSUtils.randomString] + types = [PBSUtils.randomString] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy new file mode 100644 index 00000000000..7193bb920dd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy @@ -0,0 +1,7 @@ +package org.prebid.server.functional.model.config + +class RuleEngineModelDefault { + + ResultFunction function + RuleEngineModelDefaultArgs args +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy new file mode 100644 index 00000000000..3d7884926de --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.config + +class RuleEngineModelDefaultArgs { + + String analyticsValue +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy new file mode 100644 index 00000000000..557fc999e61 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.config + +import static java.lang.Boolean.TRUE +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult + +class RuleEngineModelRule { + + List conditions + List results + + static RuleEngineModelRule createRuleEngineModelRule() { + new RuleEngineModelRule().tap { + it.conditions = [TRUE as String] + it.results = [createRuleEngineModelRuleWithExcludeResult()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy new file mode 100644 index 00000000000..cb898171623 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy @@ -0,0 +1,38 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.model.bidder.BidderName + +import static org.prebid.server.functional.model.bidder.BidderName.ACEEX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.ResultFunction.EXCLUDE_BIDDER +import static org.prebid.server.functional.model.config.ResultFunction.INCLUDE_BIDDERS +import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG + +class RuleEngineModelRuleResult { + + ResultFunction function + RuleEngineModelRuleResultsArgs args + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithIncludeResult(BidderName bidderName = ACEEX, + Boolean ifSyncedId = false) { + new RuleEngineModelRuleResult().tap { + it.function = INCLUDE_BIDDERS + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName, ifSyncedId) + } + } + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithExcludeResult(BidderName bidderName = OPENX, + Boolean ifSyncedId = false) { + new RuleEngineModelRuleResult().tap { + it.function = EXCLUDE_BIDDER + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName, ifSyncedId) + } + } + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithLogATagResult() { + new RuleEngineModelRuleResult().tap { + it.function = LOG_A_TAG + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgsOnlyATag() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy new file mode 100644 index 00000000000..c1ec70eb853 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy @@ -0,0 +1,31 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonProperty +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.response.auction.BidRejectionReason +import org.prebid.server.functional.util.PBSUtils + +class RuleEngineModelRuleResultsArgs { + + List bidders + @JsonProperty("seatnonbid") + BidRejectionReason seatNonBid + @JsonProperty("analyticsValue") + String analyticsValue + @JsonProperty("ifSyncedId") + Boolean ifSyncedId + + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgs(BidderName bidderName, Boolean ifSyncedId) { + new RuleEngineModelRuleResultsArgs().tap { + it.bidders = [bidderName] + it.analyticsValue = PBSUtils.randomString + it.ifSyncedId = ifSyncedId + } + } + + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgsOnlyATag() { + new RuleEngineModelRuleResultsArgs().tap { + it.analyticsValue = PBSUtils.randomString + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy new file mode 100644 index 00000000000..299ad225a89 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.pricefloors.Country.USA + +@ToString(includeNames = true, ignoreNulls = true) +class RuleEngineModelSchema { + + RuleEngineFunction function + RuleEngineFunctionArgs args + + static RuleEngineModelSchema createDeviceCountryInSchema(List argsCountries = [USA]) { + new RuleEngineModelSchema().tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(countries: argsCountries) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy new file mode 100644 index 00000000000..32fd4f22828 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy @@ -0,0 +1,23 @@ +package org.prebid.server.functional.model.config + +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.util.PBSUtils.randomString + +class RuleSet { + + Boolean enabled + Stage stage + String name + String version + List modelGroups + + static RuleSet createRuleSets() { + new RuleSet().tap { + it.enabled = true + it.stage = PROCESSED_AUCTION_REQUEST + it.name = randomString + it.version = randomString + it.modelGroups = [RulesEngineModelGroup.createRulesModuleGroup()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy new file mode 100644 index 00000000000..f9361465cd4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy @@ -0,0 +1,29 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonProperty +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.config.RuleEngineModelRule.createRuleEngineModelRule +import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema + +class RulesEngineModelGroup { + + Integer weight + String version + String analyticsKey + List schema + @JsonProperty("default") + List modelDefault + List rules + + static RulesEngineModelGroup createRulesModuleGroup() { + new RulesEngineModelGroup().tap { + it.weight = PBSUtils.getRandomNumber(1, 100) + it.version = PBSUtils.randomString + it.analyticsKey = PBSUtils.randomString + it.schema = [createDeviceCountryInSchema()] + it.modelDefault = [] + it.rules = [createRuleEngineModelRule()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy index 91a7e54dc37..60ef8fa7884 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy @@ -13,7 +13,7 @@ class Device { UserAgent sua String ip String ipv6 - Integer devicetype + DeviceType devicetype String make String model String os diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy new file mode 100644 index 00000000000..b33855723d7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum DeviceType { + + MOBILE_TABLET_GENERAL(1), + PERSONAL_COMPUTER(2), + CONNECTED_TV(3), + PHONE(4), + TABLET(5), + CONNECTED_DEVICE(6), + SET_TOP_BOX(7), + OOH_DEVICE(8) + + @JsonValue + final Integer value + + DeviceType(Integer value) { + this.value = value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy new file mode 100644 index 00000000000..15f737d317e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +enum ImpUnitCode { + + TAG_ID, + GPID, + PB_AD_SLOT, + STORED_REQUEST +} + diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy index 3ab6e7a6dbf..23b4e7f87a5 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -47,6 +47,8 @@ class Prebid { AlternateBidderCodes alternateBidderCodes @JsonProperty("profiles") List profileNames + @JsonProperty("kvps") + Map keyValuePairs static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy index 7976ae24c2f..a8b737a848a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy @@ -8,6 +8,7 @@ import org.prebid.server.functional.model.request.auction.FetchStatus import org.prebid.server.functional.model.request.auction.Imp import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS_BLOCK @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) @@ -20,7 +21,7 @@ class AnalyticResult { static AnalyticResult buildFromImp(Imp imp) { def appliedTo = new AppliedTo(impIds: [imp.id], bidders: [imp.ext.prebid.bidder.configuredBidders.first()]) - def impResult = new ImpResult(status: 'success-block', values: new ModuleValue(richmediaFormat: 'mraid'), appliedTo: appliedTo) + def impResult = new ImpResult(status: SUCCESS_BLOCK, values: new ModuleValue(richmediaFormat: 'mraid'), appliedTo: appliedTo) new AnalyticResult(name: 'reject-richmedia', status: SUCCESS, results: [impResult]) } } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy index 2fb7d75bbf7..98b8dafb43e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy @@ -14,6 +14,7 @@ enum BidRejectionReason { REQUEST_BLOCKED_GENERAL(200), REQUEST_BLOCKED_UNSUPPORTED_CHANNEL(201), REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE(202), + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE(203), REQUEST_BLOCKED_PRIVACY(204), REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY(205), diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy index de157de7775..8740e415f47 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy @@ -4,13 +4,14 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.FetchStatus @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) @EqualsAndHashCode class ImpResult { - String status + FetchStatus status ModuleValue values AppliedTo appliedTo } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy index 9a1e9d1b440..d17db07a968 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy @@ -1,16 +1,22 @@ package org.prebid.server.functional.model.response.auction -import com.fasterxml.jackson.databind.PropertyNamingStrategies -import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.ModuleName +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) -@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @EqualsAndHashCode class ModuleValue { - ModuleName module - String richmediaFormat + ModuleName module + String richmediaFormat + String analyticsKey + String analyticsValue + String modelVersion + String conditionFired + String resultFunction + List biddersRemoved + BidRejectionReason seatNonBid + String message } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy index 1bce783d048..fbcfa74683d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonValue enum ResponseAction { - UPDATE, NO_ACTION, NO_INVOCATION + UPDATE, NO_ACTION, NO_INVOCATION, REJECT @JsonValue String getValue() { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy index 16e1fd46459..a30917a18a6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy @@ -3,11 +3,12 @@ package org.prebid.server.functional.model.response.auction import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) class SeatNonBid { - String seat + BidderName seat List nonBid } diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index f47efd346f8..1e2c67fad6f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -274,7 +274,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == bidderName.value + assert seatNonBid.seat == bidderName assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -329,7 +329,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == bidderName.value + assert seatNonBid.seat == bidderName assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -787,7 +787,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == UNKNOWN.value + assert seatNonBid.seat == UNKNOWN assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -843,7 +843,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == UNKNOWN.value + assert seatNonBid.seat == UNKNOWN assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1187,7 +1187,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == requestedAllowedBidderCode.value + assert seatNonBid.seat == requestedAllowedBidderCode assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1242,7 +1242,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == requestedAllowedBidderCode.value + assert seatNonBid.seat == requestedAllowedBidderCode assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1303,7 +1303,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == allowedBidderCodes.value + assert seatNonBid.seat == allowedBidderCodes assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy index 0db60125603..c563796496e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -1145,7 +1145,7 @@ class BidderParamsSpec extends BaseSpec { ["No match between the configured currencies and bidRequest.cur"] def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == BidderName.GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY @@ -1242,7 +1242,7 @@ class BidderParamsSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == BidderName.ALIAS.value + assert seatNonBid.seat == BidderName.ALIAS assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY diff --git a/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy index bd636e9ccea..02adb2ab5e5 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy @@ -1,6 +1,7 @@ package org.prebid.server.functional.tests import org.mockserver.model.HttpStatusCode +import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.config.AccountAuctionConfig import org.prebid.server.functional.model.config.AccountBidValidationConfig import org.prebid.server.functional.model.config.AccountConfig @@ -22,6 +23,7 @@ import static org.mockserver.model.HttpStatusCode.OK_200 import static org.mockserver.model.HttpStatusCode.PROCESSING_102 import static org.mockserver.model.HttpStatusCode.SERVICE_UNAVAILABLE_503 import static org.prebid.server.functional.model.AccountStatus.ACTIVE + import static org.prebid.server.functional.model.config.BidValidationEnforcement.ENFORCE import static org.prebid.server.functional.model.request.auction.DebugCondition.DISABLED import static org.prebid.server.functional.model.request.auction.DebugCondition.ENABLED @@ -57,7 +59,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_NO_BID @@ -83,7 +85,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_INVALID_BID_RESPONSE } @@ -105,7 +107,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_BIDDER_UNREACHABLE } @@ -138,7 +140,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE_SIZE } @@ -169,7 +171,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE @@ -216,7 +218,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_NO_BID @@ -277,7 +279,7 @@ class SeatNonBidSpec extends BaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_TIMED_OUT } @@ -301,7 +303,7 @@ class SeatNonBidSpec extends BaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy index 73e44cc3232..1582f3b200d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy @@ -219,7 +219,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION, NO_ACTION] it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -230,7 +230,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -280,7 +280,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -291,7 +291,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -341,7 +341,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -352,7 +352,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -395,7 +395,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -433,7 +433,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION, NO_ACTION] it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -482,7 +482,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -529,7 +529,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -577,7 +577,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -626,7 +626,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -796,7 +796,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -859,7 +859,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -923,7 +923,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -934,7 +934,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -983,7 +983,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.analyticsTags.activities.name.flatten() == [ORTB2_BLOCKING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SUCCESS_ALLOW].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SUCCESS_ALLOW] it.analyticsTags.activities.results.values.module.flatten().every { it == null } } @@ -1052,7 +1052,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION, NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING, AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -1121,7 +1121,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index 453de43aa3c..f721b93eac0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -3,12 +3,16 @@ package org.prebid.server.functional.tests.module import org.prebid.server.functional.model.config.Endpoint import org.prebid.server.functional.model.config.ExecutionPlan import org.prebid.server.functional.model.config.Stage +import org.prebid.server.functional.model.response.auction.AnalyticResult +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.model.response.auction.InvocationResult import org.prebid.server.functional.tests.BaseSpec import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING -import static org.prebid.server.functional.model.ModuleName.PB_REQUEST_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RESPONSE_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER +import static org.prebid.server.functional.model.ModuleName.PB_REQUEST_CORRECTION +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.config.Endpoint.OPENRTB2_AUCTION import static org.prebid.server.functional.model.config.Stage.ALL_PROCESSED_BID_RESPONSES import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST @@ -60,4 +64,21 @@ class ModuleBaseSpec extends BaseSpec { ["hooks.${PB_REQUEST_CORRECTION.code}.enabled": "true", "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_REQUEST_CORRECTION, [stage]))] } + + protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { + ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes" : "10000", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "20000", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis": "10000", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "10000", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-factor" : "1.2", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-jitter" : "1.2", + "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_RULE_ENGINE, [stage]))] + } + + protected static List getAnalyticResults(BidResponse response) { + response.ext.prebid.modules?.trace?.stages?.first() + ?.outcomes?.first()?.groups?.first() + ?.invocationResults?.first()?.analyticsTags?.activities + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy index 2b2de98750b..a7a97bc8816 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy @@ -1521,7 +1521,7 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == ErrorType.GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_ADVERTISER_BLOCKED } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy new file mode 100644 index 00000000000..64a42194b59 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -0,0 +1,262 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.request.auction.Imp + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineAliasSpec extends RuleEngineBaseSpec { + + def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidders = [OPENX, AMX, OPENX_ALIAS, GENERIC] + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, bidders)) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == 1 + + and: "Bid response should contain seatBid.seat" + assert bidResponse.seatbid.seat == [OPENX_ALIAS] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == MULTI_BID_ADAPTERS.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 3 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == MULTI_BID_ADAPTERS.sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, OPENX_ALIAS, AMX])) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == [bidRequest.imp[1].id] + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX_ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[1].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, [ALIAS, AMX, OPENX])) + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert bidResponse.seatbid.seat == [ALIAS] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == MULTI_BID_ADAPTERS.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 3 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == MULTI_BID_ADAPTERS.sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.unique().flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, [ALIAS, AMX, OPENX])) + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved == [ALIAS] + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == [bidRequest.imp[1].id] + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[1].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy new file mode 100644 index 00000000000..57f8659682b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -0,0 +1,178 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountHooksConfiguration +import org.prebid.server.functional.model.config.PbRulesEngine +import org.prebid.server.functional.model.config.PbsModulesConfig +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.pricefloors.Country +import org.prebid.server.functional.model.request.auction.Amx +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.ImpUnitCode +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.tests.module.ModuleBaseSpec +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.config.ModuleHookImplementation.PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.GPID +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.PB_AD_SLOT +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.STORED_REQUEST +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.TAG_ID +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID + +abstract class RuleEngineBaseSpec extends ModuleBaseSpec { + + protected static final List MULTI_BID_ADAPTERS = [GENERIC, OPENX, AMX].sort() + protected static final String APPLIED_FOR_ALL_IMPS = "*" + protected static final String DEFAULT_CONDITIONS = "default" + protected final static String CALL_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.call" + protected final static String NOOP_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.noop" + protected final static String UPDATE_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.update" + protected final static Closure INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an array of strings" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_SINGLE_STRING_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be a string" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_SINGLE_INTEGER_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an integer" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an array of integers" + } + + protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", + "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", + "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", + "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final String CONFIG_DATA_CENTER = PBSUtils.randomString + private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync" + private static final Map GENERIC_CONFIG = [ + "adapters.${GENERIC.value}.usersync.redirect.url" : USER_SYNC_URL, + "adapters.${GENERIC.value}.usersync.redirect.support-cors": false as String, + "adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String] + protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService(GENERIC_CONFIG + + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) + + protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + it.ext.prebid.trace = VERBOSE + it.ext.prebid.returnAllBidStatus = true + } + } + + protected static Imp updateBidderImp(Imp imp, List bidders = MULTI_BID_ADAPTERS) { + imp.ext.prebid.bidder.tap { + openx = bidders.contains(OPENX) ? Openx.defaultOpenx : null + openxAlias = bidders.contains(OPENX_ALIAS) ? Openx.defaultOpenx : null + amx = bidders.contains(AMX) ? new Amx() : null + generic = bidders.contains(GENERIC) ? new Generic() : null + alias = bidders.contains(ALIAS) ? new Generic() : null + } + imp + } + + protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest, Country country = USA) { + bidRequest.device = new Device(geo: new Geo(country: country)) + } + + protected static Account getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { + def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) + new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) + } + + protected static BidRequest createBidRequestWithDomains(DistributionChannel type, String domain, boolean usePublisher = true) { + def request = getDefaultBidRequestWithMultiplyBidders(type) + + switch (type) { + case SITE: + if (usePublisher) request.site.publisher.domain = domain + else request.site.domain = domain + break + case APP: + if (usePublisher) request.app.publisher.domain = domain + else request.app.domain = domain + break + case DOOH: + if (usePublisher) request.dooh.publisher.domain = domain + else request.dooh.domain = domain + break + } + request + } + + protected static BidRequest updatePublisherDomain(BidRequest bidRequest, DistributionChannel distributionChannel, String domain) { + switch (distributionChannel) { + case SITE: + bidRequest.site.publisher.domain = domain + break + case APP: + bidRequest.app.publisher.domain = domain + break + case DOOH: + bidRequest.dooh.publisher.domain = domain + break + } + bidRequest + } + + protected static String getImpAdUnitCodeByCode(Imp imp, ImpUnitCode code) { + switch (code) { + case TAG_ID: + return imp.tagId + case GPID: + return imp.ext.gpid + case PB_AD_SLOT: + return imp.ext.data.pbAdSlot + case STORED_REQUEST: + return imp.ext.prebid.storedRequest.id + default: + return null + } + } + + protected static String getImpAdUnitCode(Imp imp) { + [ + imp?.ext?.gpid, + imp?.tagId, + imp?.ext?.data?.pbAdSlot, + imp?.ext?.prebid?.storedRequest?.id + ].findResult { it } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy new file mode 100644 index 00000000000..de900d10215 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy @@ -0,0 +1,1134 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.ChannelType +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.model.pricefloors.MediaType +import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.ImpExtContextData +import org.prebid.server.functional.model.request.auction.PrebidStoredRequest +import org.prebid.server.functional.model.request.auction.Publisher +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ChannelType.WEB +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN +import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.GPID +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.PB_AD_SLOT +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.STORED_REQUEST +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.TAG_ID +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineContextSpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when channel match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: CHANNEL)] + rules[0].conditions = [WEB.value] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when channel not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: CHANNEL)] + rules[0].conditions = [PBSUtils.getRandomEnum(ChannelType, [WEB]).value] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when domain match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { + updatePublisherDomain(it, distributionChannel, randomDomain) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [randomDomain] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + distributionChannel << DistributionChannel.values() + } + + def "PBS shouldn't exclude bidder when domain not match with condition"() { + given: "Default bid request with random domain" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionCahannel).tap { + updatePublisherDomain(it, distributionCahannel, PBSUtils.randomString) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + distributionCahannel << DistributionChannel.values() + } + + def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiplyB bidders" + def bidRequest = bidRequestWithDomaint + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DOMAIN_IN)) + + where: + bidRequestWithDomaint << [ + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = PBSUtils.randomString + }] + } + + def "PBS should exclude bidder when domainIn match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = createBidRequestWithDomains(type, randomDomain, usePublisher) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, randomDomain]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + type | usePublisher + SITE | true + SITE | false + APP | true + APP | false + DOOH | true + DOOH | false + } + + def "PBS shouldn't exclude bidder when domainIn not match with condition"() { + given: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = PBSUtils.randomString + }] + } + + def "PBS should exclude bidder when bundle match with condition"() { + given: "Default bid request with multiply bidder" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = bundle + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [bundle] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when bundle not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, BUNDLE_IN)) + } + + def "PBS should exclude bidder when bundleIn match with condition"() { + given: "Default bid request with multiply bidders" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = bundle + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [bundle]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [mediaTypeInArgs]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, MEDIA_TYPE_IN)) + + where: + mediaTypeInArgs << [null, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when mediaTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Setup bidder response" + def bidderResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidderResponse) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [BANNER.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(MediaType, [BANNER])]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when adUnitCode match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].ext.gpid = gpid + imp[0].tagId = tagId + imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) + imp[0].ext.prebid.storedRequest = prebidStoredRequest + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [getImpAdUnitCode(bidRequest.imp[0])] + } + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gpid | tagId | pbAdSlot | prebidStoredRequest + PBSUtils.getRandomString() | null | null | null + null | PBSUtils.getRandomString() | null | null + null | null | PBSUtils.getRandomString() | null + null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + PBSUtils.getRandomString() | PBSUtils.getRandomString() | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | PBSUtils.getRandomString() | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | null | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + } + + def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].tagId = PBSUtils.randomString + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest).tap { + impData = Imp.getDefaultImpression() + } + storedImpDao.save(storedImp) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [arguments]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, AD_UNIT_CODE_IN)) + + where: + arguments << [PBSUtils.randomBoolean, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when adUnitCodeIn match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].tagId = PBSUtils.randomString + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, + getImpAdUnitCodeByCode(bidRequest.imp[0], impUnitCode)]) + } + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + impUnitCode << [TAG_ID, GPID, PB_AD_SLOT, STORED_REQUEST] + } + + def "PBS shouldn't exclude bidder when adUnitCodeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy new file mode 100644 index 00000000000..4779f77af36 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -0,0 +1,870 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineModelDefault +import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs +import org.prebid.server.functional.model.config.RuleSet +import org.prebid.server.functional.model.config.RulesEngineModelGroup +import org.prebid.server.functional.model.config.Stage +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithLogATagResult +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.ERROR_NO_BID +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineCoreSpec extends RuleEngineBaseSpec { + + def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics key"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets without analytics value" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].analyticsKey = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metric" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBs should populate call and update metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[CALL_METRIC] == 1 + assert metrics[UPDATE_METRIC] == 1 + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should remove bidder from imps and use default 203 value for seatNonBid when seatNonBid null and exclude bidder in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression)) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid.impId.sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression)) + updateBidRequestWithGeoCountry(it) + ext.prebid.tap { + returnAllBidStatus = false + trace = VERBOSE + } + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = ERROR_NO_BID + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response shouldn't populate seatNon bid with code 203" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == ERROR_NO_BID + it.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS shouldn't include unknown bidder when unknown bidder specified in result account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(UNKNOWN)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == 0 + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + } + + def "PBS shouldn't exclude unknown bidder when unknown bidder specified in result account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(UNKNOWN)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + } + + def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat == [OPENX] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [GENERIC, AMX].sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [GENERIC, AMX].sort() + assert seatNonBid.nonBid.impId.flatten() == [bidRequest.imp[0].id, bidRequest.imp[0].id] + assert seatNonBid.nonBid.statusCode.flatten() == + [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seatBids" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid.impId.sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should leave only include bidder at imps when bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, AMX])) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat == [GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == [AMX, OPENX] + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [AMX, OPENX].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should only logATag when present only function log a tag"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, AMX])) + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithLogATagResult()] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + def impResult = result.results[0] + verifyAll(impResult) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + + it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + } + + verifyAll(impResult) { + !it.values.biddersRemoved + !it.values.seatNonBid + } + } + + def "PBS should remove bidder and update analytics when first rule sets disabled and second enabled in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets.first.enabled = false + it.ruleSets.add(RuleSet.createRuleSets()) + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should skip rule set and take next one when rule sets not a processed auction request"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules engine and several rule sets" + def firstResults = [createRuleEngineModelRuleWithExcludeResult(GENERIC), + createRuleEngineModelRuleWithExcludeResult(AMX), + createRuleEngineModelRuleWithExcludeResult(OPENX)] + def secondResult = [createRuleEngineModelRuleWithExcludeResult(AMX)] + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = firstResults + it.ruleSets[0].stage = stage as Stage + it.ruleSets.add(RuleSet.createRuleSets()) + it.ruleSets[1].modelGroups[0].rules[0].results = secondResult + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == AMX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + stage << Stage.values() - PROCESSED_AUCTION_REQUEST + } + + def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with few model group" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + it.weight = 1 + it.rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + } + it.ruleSets[0].modelGroups.add(RulesEngineModelGroup.createRulesModuleGroup()) + it.ruleSets[0].modelGroups[1].tap { + it.weight = 100 + it.rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + } + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[1] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't log the default model group and should modify response when other rule fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with default model" + def analyticsValue = PBSUtils.randomString + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: analyticsValue))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it, BULGARIA) + } + + and: "Account with default model" + def analyticsValue = PBSUtils.randomString + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: analyticsValue))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + def impResult = result.results[0] + verifyAll(impResult) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == analyticsValue + it.values.resultFunction == LOG_A_TAG.value + it.values.conditionFired == DEFAULT_CONDITIONS + it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + } + + and: "Analytics imp result shouldn't contain remove info" + verifyAll(impResult) { + !it.values.biddersRemoved + !it.values.seatNonBid + } + } + + def "PBS shouldn't log the default model group and modify response when rules fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with default model" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: PBSUtils.randomString))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result should contain detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy new file mode 100644 index 00000000000..d8eabbaf60d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy @@ -0,0 +1,434 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.pricefloors.Country +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.DeviceType +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineDeviceSpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when deviceCountry match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] + rules[0].conditions = [USA.toString()] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = DEVICE_COUNTRY + rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when deviceType match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [deviceType.value as String] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceType not match with condition"() { + given: "Default bid request with multiply bidders" + def requestDeviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: requestDeviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType, [requestDeviceType]).value as String] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the deviceCountryIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.randomString]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DEVICE_COUNTRY_IN)) + } + + def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomString()]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING(bidRequest.accountId, DEVICE_TYPE_IN)) + } + + def "PBS should exclude bidder when deviceTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [deviceType.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(DeviceType).value as String]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy new file mode 100644 index 00000000000..542cad335ca --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy @@ -0,0 +1,264 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { + + def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(countries: [CONFIG_DATA_CENTER]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DATA_CENTER_IN)) + } + + def "PBS should exclude bidder when dataCenterIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [CONFIG_DATA_CENTER]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when dataCenter match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [CONFIG_DATA_CENTER] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when dataCenter not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy new file mode 100644 index 00000000000..3f63a04ec24 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy @@ -0,0 +1,909 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.request.GppSectionId +import org.prebid.server.functional.model.request.auction.AppExt +import org.prebid.server.functional.model.request.auction.AppExtData +import org.prebid.server.functional.model.request.auction.Content +import org.prebid.server.functional.model.request.auction.Data +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.SiteExt +import org.prebid.server.functional.model.request.auction.SiteExtData +import org.prebid.server.functional.model.request.auction.User +import org.prebid.server.functional.model.request.auction.UserExt +import org.prebid.server.functional.model.request.auction.UserExtData +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.TcfConsent + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS + +class RuleEnginePrivacySpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when eidAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: EID_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: eids) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = EID_AVAILABLE + rules[0].conditions = ["TRUE"] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + eids << [null, []] + } + + def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomNumber]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, EID_IN)) + } + + def "PBS should exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def eid = Eid.getDefaultEid() + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [eid]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, eid.source, PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when userFpdAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = requestedUfpUser + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(data: [Data.defaultData])] + } + + def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = requestedUfpUser + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + requestedUfpUser << [new User(data: null), new User(data: [null]), + new User(ext: new UserExt(data: null)), + new User(data: null, ext: new UserExt(data: null)) + ] + } + + def "PBS should exclude bidder when fpdAvailable match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(ext: new UserExt(data: UserExtData.FPDUserExtData)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.ext = new SiteExt(data: SiteExtData.FPDSiteExtData) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) + } + ] + } + + def "PBS shouldn't exclude bidder when fpdAvailable not match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(data: null, ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.content = new Content(data: [null]) + site.ext = new SiteExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.ext = new SiteExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.content = new Content(data: [null]) + app.ext = new AppExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.ext = new AppExt(data: null) + } + ] + } + + def "PBS should exclude bidder when gppSidAvailable match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: gppSid) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gppSid << [[PBSUtils.randomNegativeNumber], null] + } + + def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: 0, gppSid: [PBSUtils.getRandomEnum(GppSectionId, [GppSectionId.TCF_EU_V2]).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING(bidRequest.accountId, GPP_SID_IN)) + } + + def "PBS should exclude bidder when gppSidIn match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [gppSectionId]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 + } + + def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.getRandomEnum(GppSectionId, [gppSectionId]).getIntValue()]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 + } + + def "PBS should exclude bidder when tcfInScope match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gdpr | condition + 1 | 'true' + 0 | 'false' + } + + def "PBS shouldn't exclude bidder when tcfInScope not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gdpr | condition + 0 | 'true' + 1 | 'false' + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy new file mode 100644 index 00000000000..f8c47e4e13e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy @@ -0,0 +1,319 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.PERCENT +import static org.prebid.server.functional.model.config.RuleEngineFunction.PREBID_KEY +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineSpecialSpec extends RuleEngineBaseSpec { + + def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.randomString) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result should contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_SINGLE_INTEGER_LOG_WARNING(bidRequest.accountId, PERCENT)) + } + + def "PBS should exclude bidder when percent match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.getRandomNumber(100)) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when percent less than zero"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: percent) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + percent << [0, PBSUtils.randomNegativeNumber] + } + + def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomNumber) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response should not contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_SINGLE_STRING_LOG_WARNING(bidRequest.accountId, PREBID_KEY)) + } + + def "PBS should exclude bidder when prebidKey match with condition"() { + given: "Default bid request with multiply bidder" + def keyField = "key" + def keyString = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + ext.prebid.keyValuePairs = [(keyString): keyField] + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs((keyField): keyString) + } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [keyField] + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { + given: "Default bid request with multiply bidder" + def key = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + ext.prebid.keyValuePairs = [(key): PBSUtils.randomString] + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: key) + } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [PBSUtils.randomString] + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy new file mode 100644 index 00000000000..eb4f57707bd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -0,0 +1,298 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.UidsCookie +import org.prebid.server.functional.util.HttpUtil + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineSyncSpec extends RuleEngineBaseSpec { + + def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, true)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX] + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should contain seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, false)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, true)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should remove requested bidders at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, true)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response shouldn't contain seat" + assert bidResponse.seatbid.seat == [GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [AMX, OPENX].sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should contain seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [OPENX, AMX].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS shouldn't include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, false)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response shouldn't contain seat" + assert !bidResponse.seatbid.seat + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [OPENX, AMX, GENERIC].sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response shouldn't contain seatNon bid with code 203" + assert !bidResponse.ext.seatnonbid + } + + def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, true)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy new file mode 100644 index 00000000000..f3271297895 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy @@ -0,0 +1,434 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE +import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA + +class RuleEngineValidationSpec extends RuleEngineBaseSpec { + + def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBs should populate call and noop metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[CALL_METRIC] == 1 + assert metrics[NOOP_METRIC] == 1 + + and: "PBs should populate update metrics" + assert !metrics[UPDATE_METRIC] + + where: + pbRulesEngine << [ + createRulesEngineWithRule().tap { it.ruleSets = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].stage = null }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] } + ] + } + + def "PBS shouldn't remove bidder when rule engine not fully configured in account without rule conditions"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBs should populate noop metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[NOOP_METRIC] == 1 + } + + def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results[0].args = null } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should populate failer metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[NOOP_METRIC] == 1 + } + + def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRulesEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should emit failed logs" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account $bidRequest.accountId:" + + " Weighted list cannot be empty") + } + + def "PBS shouldn't log default model when rule does not fired and empty model default"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it, BULGARIA) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + pbRuleEngine << [createRulesEngineWithRule(false), null] + } + + def "PBS shouldn't remove bidder when rule sets disabled in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with disabled rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets.first.enabled = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + } + + def "PBS shouldn't remove any bidder without cache account request"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule() + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBs should emit failed logs" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Parsing rule for account $bidRequest.accountId").size() == 1 + } + + def "PBS shouldn't take rule with higher weight and not remove bidder when weight negative or zero"() { + given: "Start up time" + def start = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with few model group" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + it.weight = weight + } + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBS should emit log" + def logsByTime = pbsServiceWithRulesEngineModule.getLogsByTime(start) + assert getLogsByText(logsByTime, "Failed to parse rule-engine config for account $bidRequest.accountId:" + + " Weight must be greater than zero") + + where: + weight << [PBSUtils.randomNegativeNumber, 0] + } + + def "PBS should reject processing rule engine when #function schema function contain args"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = function + it.args = RuleEngineFunctionArgs.defaultFunctionArgs + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${function.value}' configuration is invalid: No arguments allowed") + + where: + function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, + USER_FPD_AVAILABLE, EID_AVAILABLE, CHANNEL, DEVICE_COUNTRY] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy index 7c6e90d263e..517da393668 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy @@ -29,7 +29,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { private static final String PATTERN_NAME_ACCOUNT = PBSUtils.randomString private static final Map DISABLED_FILTER_SPECIFIC_PATTERN_NAME_CONFIG = getRichMediaFilterSettings(PATTERN_NAME, false) private static final Map SPECIFIC_PATTERN_NAME_CONFIG = getRichMediaFilterSettings(PATTERN_NAME) - private static final Map SNAKE_SPECIFIC_PATTERN_NAME_CONFIG = (getRichMediaFilterSettings(PATTERN_NAME) + + private static final Map SNAKE_SPECIFIC_PATTERN_NAME_CONFIG = (getRichMediaFilterSettings(PATTERN_NAME) + ["hooks.host-execution-plan": encode(ExecutionPlan.getSingleEndpointExecutionPlan(OPENRTB2_AUCTION, PB_RICHMEDIA_FILTER, [ALL_PROCESSED_BID_RESPONSES]).tap { endpoints.values().first().stages.values().first().groups.first.hookSequenceSnakeCase = [new HookId(moduleCodeSnakeCase: PB_RICHMEDIA_FILTER.code, hookImplCodeSnakeCase: "${PB_RICHMEDIA_FILTER.code}-${ALL_PROCESSED_BID_RESPONSES.value}-hook")] })]).collectEntries { key, value -> [(key.toString()): value.toString()] } @@ -155,7 +155,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -239,7 +239,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -323,7 +323,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -440,7 +440,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -453,10 +453,4 @@ class RichMediaFilterSpec extends ModuleBaseSpec { where: admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}.${PBSUtils.randomString}"] } - - private static List getAnalyticResults(BidResponse response) { - response.ext.prebid.modules?.trace?.stages?.first() - ?.outcomes?.first()?.groups?.first() - ?.invocationResults?.first()?.analyticsTags?.activities - } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy index 4e9bc40b590..b586688398b 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy @@ -967,7 +967,7 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_PRICE_FLOOR assert seatNonBid.nonBid.size() == bidResponse.seatbid[0].bid.size() @@ -1214,7 +1214,7 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_PRICE_FLOOR assert seatNonBid.nonBid.size() == bidResponse.seatbid[0].bid.size() diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy index 2575789049d..c2bf06fe50a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.tests.privacy +import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.config.AccountDsaConfig import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest @@ -14,6 +15,7 @@ import org.prebid.server.functional.model.response.auction.DsaResponse as BidDsa import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent + import static org.prebid.server.functional.model.request.auction.DsaPubRender.PUB_CANT_RENDER import static org.prebid.server.functional.model.request.auction.DsaPubRender.PUB_WILL_RENDER import static org.prebid.server.functional.model.request.auction.DsaRequired.NOT_REQUIRED @@ -315,7 +317,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA @@ -495,7 +497,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA @@ -535,7 +537,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy index 5fc0f5d7bca..299d911a398 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy @@ -267,7 +267,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_PRIVACY From 471a317fb44f9eff4785f24a561817f0c83d9876 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Sun, 5 Oct 2025 19:50:18 +0300 Subject: [PATCH 88/94] fix tests --- .../functional/model/response/auction/ModuleValue.groovy | 2 ++ .../tests/module/pbruleengine/RuleEngineBaseSpec.groovy | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy index d17db07a968..c780efaa422 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.model.response.auction +import com.fasterxml.jackson.annotation.JsonProperty import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.ModuleName @@ -10,6 +11,7 @@ import org.prebid.server.functional.model.bidder.BidderName class ModuleValue { ModuleName module + @JsonProperty("richmedia-format") String richmediaFormat String analyticsKey String analyticsValue diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 57f8659682b..81443fa9070 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -87,8 +87,17 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService(GENERIC_CONFIG + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) + def setup() { + bidder.setResponse() + } + + def cleanup() { + bidder.reset() + } + protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.tmax = 5_000 // prevents timeout issues on slow pipelines it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() From c30f6241738534bcd7692cd653b53d6c90e723b5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Sun, 5 Oct 2025 21:44:11 +0300 Subject: [PATCH 89/94] Added jackson parsing module for instant. --- pom.xml | 1 - src/main/java/org/prebid/server/json/ObjectMapperProvider.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d682c15576..55db6df3ed6 100644 --- a/pom.xml +++ b/pom.xml @@ -149,7 +149,6 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - test com.fasterxml.jackson.dataformat diff --git a/src/main/java/org/prebid/server/json/ObjectMapperProvider.java b/src/main/java/org/prebid/server/json/ObjectMapperProvider.java index fc1bcea0611..e34ff41c0c0 100644 --- a/src/main/java/org/prebid/server/json/ObjectMapperProvider.java +++ b/src/main/java/org/prebid/server/json/ObjectMapperProvider.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.module.blackbird.BlackbirdModule; public final class ObjectMapperProvider { @@ -23,6 +24,7 @@ public final class ObjectMapperProvider { .build() .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .registerModule(new JavaTimeModule()) .registerModule(new BlackbirdModule()) .registerModule(new ZonedDateTimeModule()) .registerModule(new MissingJsonNodeModule()) From 39fd337aded72e125eaaa1dec08ff47896b430b5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Mon, 6 Oct 2025 11:16:03 +0300 Subject: [PATCH 90/94] Added debug log for successfull parsing. --- .../hooks/modules/rule/engine/core/config/RuleParser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java index 9933eae0862..3136a0cefca 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/RuleParser.java @@ -121,6 +121,8 @@ private ParsingAttempt tryRegisteringNewAttempt(ParsingAttempt previousAttempt, private void succeedParsingAttempt(String accountId, PerStageRule result) { accountIdToRules.put(accountId, result); accountIdToParsingAttempt.remove(accountId); + + logger.debug("Successfully parsed rule-engine config for account {}", accountId); } private void failParsingAttempt(String accountId, ParsingAttempt attempt, Throwable cause) { From 90a202fb34507eb0ad0795640c52721d1d2010f6 Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 6 Oct 2025 13:01:12 +0300 Subject: [PATCH 91/94] Update caching mechanism for function tests --- .../pbruleengine/RuleEngineAliasSpec.groovy | 8 +-- .../pbruleengine/RuleEngineBaseSpec.groovy | 35 +++++++------ .../pbruleengine/RuleEngineContextSpec.groovy | 52 ++++++++++++------- .../pbruleengine/RuleEngineCoreSpec.groovy | 30 +++++------ .../pbruleengine/RuleEngineDeviceSpec.groovy | 19 +++---- .../RuleEngineInfrastructureSpec.groovy | 10 ++-- .../pbruleengine/RuleEnginePrivacySpec.groovy | 38 ++++++++------ .../pbruleengine/RuleEngineSpecialSpec.groovy | 14 ++--- .../pbruleengine/RuleEngineSyncSpec.groovy | 10 ++-- .../RuleEngineValidationSpec.groovy | 17 +++--- 10 files changed, 130 insertions(+), 103 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy index 64a42194b59..4477e6d9d38 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -32,7 +32,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -94,7 +94,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -154,7 +154,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -216,7 +216,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 81443fa9070..67ea20ebac6 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -72,6 +72,7 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { "Field '$functionType.fieldName' is required and has to be an array of integers" } + protected static final Map ENABLED_DEBUG_LOG_MODE = ["logging.level.root": "debug"] protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", @@ -85,19 +86,11 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { "adapters.${GENERIC.value}.usersync.redirect.support-cors": false as String, "adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String] protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService(GENERIC_CONFIG + - getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) - - def setup() { - bidder.setResponse() - } - - def cleanup() { - bidder.reset() - } + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER] + + ENABLED_DEBUG_LOG_MODE) protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.tmax = 5_000 // prevents timeout issues on slow pipelines it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() @@ -177,11 +170,21 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } protected static String getImpAdUnitCode(Imp imp) { - [ - imp?.ext?.gpid, - imp?.tagId, - imp?.ext?.data?.pbAdSlot, - imp?.ext?.prebid?.storedRequest?.id - ].findResult { it } + [imp?.ext?.gpid, imp?.tagId, imp?.ext?.data?.pbAdSlot, imp?.ext?.prebid?.storedRequest?.id] + .findResult { it } + } + + protected static waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) { + PBSUtils.waitUntil({ + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + pbsServiceWithRulesEngineModule.isContainLogsByValue("Successfully parsed rule-engine config for account $bidRequest.accountId") + }) + } + + protected static waitUntilFailedParsedAndCacheAccount(bidRequest) { + PBSUtils.waitUntil({ + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + pbsServiceWithRulesEngineModule.isContainLogsByValue("Failed to parse rule-engine config for account $bidRequest.accountId") + }) } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy index de900d10215..5bea040218e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy @@ -59,7 +59,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -119,7 +119,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -161,7 +161,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -226,7 +226,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -271,7 +271,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -336,7 +336,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -402,7 +402,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -465,7 +465,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -527,7 +527,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -570,6 +570,9 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilFailedParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -614,7 +617,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -676,7 +679,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -717,6 +720,9 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + waitUntilFailedParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -764,8 +770,8 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Account cache" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -824,8 +830,8 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Account cache" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -872,8 +878,8 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Account cache" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -943,8 +949,8 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Account cache" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -996,6 +1002,9 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + waitUntilFailedParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1050,7 +1059,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1112,6 +1121,9 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index 4779f77af36..8e9de1807c9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -41,7 +41,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) and: "Flush metric" flushMetrics(pbsServiceWithRulesEngineModule) @@ -91,7 +91,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -155,7 +155,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -208,7 +208,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -241,7 +241,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -274,7 +274,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -334,7 +334,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -393,7 +393,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -451,7 +451,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -510,7 +510,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -574,7 +574,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -642,7 +642,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -702,7 +702,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -762,7 +762,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -822,7 +822,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy index d8eabbaf60d..229e54a0ff0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy @@ -6,6 +6,7 @@ import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.DeviceType import org.prebid.server.functional.util.PBSUtils +import spock.lang.RepeatUntilFailure import java.time.Instant @@ -43,7 +44,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -104,8 +105,8 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Cache account" + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -147,7 +148,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -210,7 +211,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -254,7 +255,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -302,7 +303,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -348,7 +349,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -410,7 +411,7 @@ class RuleEngineDeviceSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy index 542cad335ca..f710ba39f93 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy @@ -38,7 +38,7 @@ class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -81,7 +81,7 @@ class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -141,7 +141,7 @@ class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -180,7 +180,7 @@ class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -240,7 +240,7 @@ class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy index 3f63a04ec24..c4f207544b5 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy @@ -55,7 +55,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -117,7 +117,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -163,6 +163,9 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilFailedParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -207,7 +210,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -250,7 +253,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS shouldn't exclude bidder when eidIn match with condition"() { + def "PBS shouldn't exclude bidder when eidIn not match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { user = new User(eids: [Eid.getDefaultEid()]) @@ -269,7 +272,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -307,7 +310,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -371,7 +374,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -410,7 +413,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -486,7 +489,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -548,7 +551,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -607,7 +610,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -653,6 +656,9 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilFailedParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -696,7 +702,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -761,7 +767,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -809,7 +815,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -879,8 +885,8 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Cache account" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy index f8c47e4e13e..cce9631e1b9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy @@ -37,7 +37,7 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -80,7 +80,7 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -140,7 +140,7 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -185,7 +185,7 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -233,7 +233,7 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -296,6 +296,9 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -315,5 +318,4 @@ class RuleEngineSpecialSpec extends RuleEngineBaseSpec { and: "Analytics result shouldn't contain info about module exclude" assert !getAnalyticResults(bidResponse) } - } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy index eb4f57707bd..881a8211ca4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -32,7 +32,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) @@ -89,7 +89,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) @@ -125,7 +125,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -164,7 +164,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) @@ -224,7 +224,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy index f3271297895..dbf7395c1f0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy @@ -32,7 +32,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) and: "Flush metrics" flushMetrics(pbsServiceWithRulesEngineModule) @@ -86,7 +86,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) and: "Flush metrics" flushMetrics(pbsServiceWithRulesEngineModule) @@ -127,7 +127,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) and: "Flush metrics" flushMetrics(pbsServiceWithRulesEngineModule) @@ -168,7 +168,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -206,7 +206,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -274,6 +274,9 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + waitUntilSuccessfullyParsedAndCacheAccount(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -351,7 +354,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -401,7 +404,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { accountDao.save(accountWithRulesEngine) and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + waitUntilFailedParsedAndCacheAccount(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) From b2c25bb0fdcd7ceb82c08f20d237c778438daa14 Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 6 Oct 2025 13:53:58 +0300 Subject: [PATCH 92/94] Add @Retry annotation for rule engine base spec --- .../tests/module/pbruleengine/RuleEngineBaseSpec.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 67ea20ebac6..0d8ebd1ee9a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -19,6 +19,7 @@ import org.prebid.server.functional.model.request.auction.ImpUnitCode import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.PBSUtils +import spock.lang.Retry import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.ALIAS @@ -40,6 +41,7 @@ import static org.prebid.server.functional.model.request.auction.TraceLevel.VERB import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +@Retry abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final List MULTI_BID_ADAPTERS = [GENERIC, OPENX, AMX].sort() From 6ed12b2483dbf255cf9f927e3be88055f0703882 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 6 Oct 2025 13:54:05 +0300 Subject: [PATCH 93/94] Add retry for rule engine tests --- .../tests/module/pbruleengine/RuleEngineBaseSpec.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 0d8ebd1ee9a..8d07db180b9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -41,7 +41,7 @@ import static org.prebid.server.functional.model.request.auction.TraceLevel.VERB import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID -@Retry +@Retry //TODO remove in 3.34+ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final List MULTI_BID_ADAPTERS = [GENERIC, OPENX, AMX].sort() @@ -172,7 +172,10 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } protected static String getImpAdUnitCode(Imp imp) { - [imp?.ext?.gpid, imp?.tagId, imp?.ext?.data?.pbAdSlot, imp?.ext?.prebid?.storedRequest?.id] + [imp?.ext?.gpid, + imp?.tagId, + imp?.ext?.data?.pbAdSlot, + imp?.ext?.prebid?.storedRequest?.id] .findResult { it } } From 301023f736d58c88b86a84c101187d4873153595 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 6 Oct 2025 15:18:58 +0300 Subject: [PATCH 94/94] prevents timeout issues on slow pipelines --- .../tests/module/pbruleengine/RuleEngineBaseSpec.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 8d07db180b9..d27d44e0fc0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -93,6 +93,7 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.tmax = 5_000 // prevents timeout issues on slow pipelines it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic()