Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum ModuleName {
PB_RESPONSE_CORRECTION ("pb-response-correction"),
ORTB2_BLOCKING("ortb2-blocking"),
PB_REQUEST_CORRECTION('pb-request-correction'),
OPTABLE_TARGETING('optable-targeting'),
PB_RULE_ENGINE('pb-rule-engine')

@JsonValue
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.prebid.server.functional.model.config

import groovy.transform.ToString

@ToString(includeNames = true, ignoreNulls = true)
class Audience {

String provider
List<AudienceId> ids
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.prebid.server.functional.model.config

import groovy.transform.ToString

@ToString(includeNames = true, ignoreNulls = true)
class AudienceId {

String id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.prebid.server.functional.model.config

import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.annotation.JsonNaming
import groovy.transform.ToString
import org.prebid.server.functional.util.PBSUtils

@ToString(includeNames = true, ignoreNulls = true)
@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy)
class CacheProperties {

Boolean enabled
Integer ttlSeconds

static CacheProperties getDefault() {
new CacheProperties().tap {
enabled = true
ttlSeconds = PBSUtils.randomNumber
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ExecutionGroup {

static ExecutionGroup getModuleExecutionGroup(ModuleName name, Stage stage) {
new ExecutionGroup().tap {
timeout = 100
timeout = 1000
hookSequence = [new HookId(moduleCode: name.code, hookImplCode: ModuleHookImplementation.forValue(name, stage).code)]
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.prebid.server.functional.model.config

import com.fasterxml.jackson.annotation.JsonValue

import static org.prebid.server.functional.model.config.OperatingSystem.ANDROID
import static org.prebid.server.functional.model.config.OperatingSystem.FIRE
import static org.prebid.server.functional.model.config.OperatingSystem.IOS
import static org.prebid.server.functional.model.config.OperatingSystem.ROKU
import static org.prebid.server.functional.model.config.OperatingSystem.TIZEN

enum IdentifierType {

EMAIL_ADDRESS("e"),
PHONE_NUMBER("p"),
POSTAL_CODE("z"),
APPLE_IDFA("a"),
GOOGLE_GAID("g"),
ROKU_RIDA("r"),
SAMSUNG_TIFA("s"),
AMAZON_AFAI("f"),
NET_ID("n"),
ID5("id5"),
UTIQ("utiq"),
OPTABLE_VID("v")

@JsonValue
final String value

IdentifierType(String value) {
this.value = value
}

static IdentifierType fromOS(OperatingSystem os) {
switch (os) {
case IOS:
return APPLE_IDFA
case ANDROID:
return GOOGLE_GAID
case ROKU:
return ROKU_RIDA
case TIZEN:
return SAMSUNG_TIFA
case FIRE:
return AMAZON_AFAI
default:
throw new IllegalArgumentException("Unsupported OS: " + os);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
OPTABLE_TARGETING_PROCESSED_AUCTION_REQUEST("optable-targeting-processed-auction-request-hook"),
PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST("pb-rule-engine-processed-auction-request")

@JsonValue
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.prebid.server.functional.model.config

import com.fasterxml.jackson.annotation.JsonValue

enum OperatingSystem {

IOS("ios"),
ANDROID("android"),
ROKU("roku"),
TIZEN("tizen"),
FIRE("fire")

@JsonValue
final String value

OperatingSystem(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.prebid.server.functional.model.config

import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.annotation.JsonNaming
import groovy.transform.ToString
import org.prebid.server.functional.util.PBSUtils

@ToString(includeNames = true, ignoreNulls = true)
@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy)
class OptableTargetingConfig {

String apiEndpoint
String apiKey
String tenant
String origin
Comment on lines +13 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apiKey, tenant, origin, unused fields, add it into default method or remove

Map<IdentifierType, String> ppidMapping
Boolean adserverTargeting
Long timeout
String idPrefixOrder
CacheProperties cache

static OptableTargetingConfig getDefault(Map<IdentifierType, String> ppidMapping) {
new OptableTargetingConfig().tap {
it.apiKey = PBSUtils.randomString
it.tenant = PBSUtils.randomString
it.origin = PBSUtils.randomString
it.apiEndpoint = PBSUtils.randomString
it.adserverTargeting = true
it.ppidMapping = ppidMapping
it.cache = CacheProperties.default
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ class PbsModulesConfig {
Ortb2BlockingConfig ortb2Blocking
PbResponseCorrection pbResponseCorrection
PbRequestCorrectionConfig pbRequestCorrection
OptableTargetingConfig optableTargeting
PbRulesEngine pbRuleEngine
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.prebid.server.functional.model.config

import groovy.transform.ToString
import org.prebid.server.functional.model.request.auction.User

@ToString(includeNames = true, ignoreNulls = true)
class TargetingOrtb {

User user
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.prebid.server.functional.model.config

import groovy.transform.ToString

@ToString(includeNames = true, ignoreNulls = true)
class TargetingResult {

List<Audience> audience
TargetingOrtb ortb2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.prebid.server.functional.testcontainers.scaffolding

import org.mockserver.matchers.TimeToLive
import org.mockserver.matchers.Times
import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpStatusCode
import org.prebid.server.functional.model.config.Audience
import org.prebid.server.functional.model.config.AudienceId
import org.prebid.server.functional.model.config.IdentifierType
import org.prebid.server.functional.model.config.OptableTargetingConfig
import org.prebid.server.functional.model.config.TargetingOrtb
import org.prebid.server.functional.model.config.TargetingResult
import org.prebid.server.functional.model.request.auction.BidRequest
import org.prebid.server.functional.model.request.auction.User
import org.prebid.server.functional.util.PBSUtils
import org.testcontainers.containers.MockServerContainer

import java.nio.charset.StandardCharsets

import static org.mockserver.model.HttpRequest.request
import static org.mockserver.model.HttpResponse.response
import static org.mockserver.model.HttpStatusCode.NO_CONTENT_204
import static org.mockserver.model.HttpStatusCode.OK_200

class StoredCache extends NetworkScaffolding {

private static final String CACHE_ENDPOINT = "/stored-cache"

StoredCache(MockServerContainer mockServerContainer) {
super(mockServerContainer, CACHE_ENDPOINT)
}

@Override
protected HttpRequest getRequest(String impId) {}

@Override
HttpRequest getRequest() {
request().withMethod("GET")
.withPath(endpoint)
}

@Override
void setResponse() {}

TargetingResult setTargetingResponse(BidRequest bidRequest, OptableTargetingConfig config) {
def targetingResult = getBodyByRequest(bidRequest)
mockServerClient.when(request()
.withMethod("GET")
.withPath("$endpoint${QueryBuilder.buildQuery(bidRequest, config)}"), Times.unlimited(), TimeToLive.unlimited(), -10)
.respond { response().withStatusCode(OK_200.code()).withBody(encode(targetingResult)) }
targetingResult
}

TargetingResult setCachedTargetingResponse(BidRequest bidRequest) {
def targetingResult = getBodyByRequest(bidRequest)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better to direct pass targetingResult without returning the same object?

mockServerClient.when(request()
.withMethod("GET")
.withPath(endpoint), Times.unlimited(), TimeToLive.unlimited(), -10)
.respond { response().withStatusCode(OK_200.code()).withBody(encode(targetingResult)) }
targetingResult
}

void setCachingResponse(HttpStatusCode statusCode = NO_CONTENT_204) {
mockServerClient.when(request()
.withMethod("POST")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that should be POST here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, that for BasicPbcStorageService.storeEntry

.withPath(endpoint), Times.unlimited(), TimeToLive.unlimited(), -10)
.respond { response().withStatusCode(statusCode.code()) }
}

private static TargetingResult getBodyByRequest(BidRequest bidRequest) {
new TargetingResult().tap {
it.audience = [new Audience(ids: [new AudienceId(id: PBSUtils.randomString)], provider: PBSUtils.randomString)]
it.ortb2 = new TargetingOrtb(user: new User(data: bidRequest.user.data, eids: bidRequest.user.eids))
}
}

private class QueryBuilder {

static String buildQuery(BidRequest bidRequest, OptableTargetingConfig config) {
buildIdsString(config) + buildAttributesString(bidRequest, config)
}

private static String buildIdsString(OptableTargetingConfig config) {
def ppids = config.ppidMapping
if (!ppids) {
return ''
}

def reorderedIds = reorderIds(ppids.keySet(), config.idPrefixOrder)

reorderedIds.collect { id ->
def value = ppids[id]
"&id=${URLEncoder.encode("${id.value}:${value}", StandardCharsets.UTF_8)}"
}.join('')
}

private static Set<IdentifierType> reorderIds(Set<IdentifierType> ids, String idPrefixOrder) {
if (!idPrefixOrder) {
return ids
}
def prefixOrder = idPrefixOrder.split(',') as List
def prefixToPriority = prefixOrder.collectEntries { v, i -> [(v): i] }
ids.sort { prefixToPriority.get(it.value, Integer.MAX_VALUE) }
}

private static String buildAttributesString(BidRequest bidRequest, OptableTargetingConfig config) {
def regs = bidRequest.regs
def gdpr = regs?.gdpr
def gdprConsent = bidRequest.user?.consent

[gdprConsent != null ? "&gdpr_consent=${gdprConsent}" : null,
"&gdpr=${gdpr ? 1 : 0}",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be regs?.gdpr in line(meaning no need variable for it)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be either 1 or 0, but with regs?.gdpr, it can be null.

regs?.gpp ? "&gpp=${regs.gpp}" : null,
regs?.gppSid ? "&gpp_sid=${regs.gppSid.first()}" : null,
config?.timeout ? "&timeout=${config.timeout}ms" : null].findAll().join('')
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ 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 org.prebid.server.functional.util.PBSUtils

import static org.prebid.server.functional.model.ModuleName.OPTABLE_TARGETING
import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING
import static org.prebid.server.functional.model.ModuleName.PB_RESPONSE_CORRECTION
import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER
Expand All @@ -16,6 +18,7 @@ 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
import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer

class ModuleBaseSpec extends BaseSpec {

Expand Down Expand Up @@ -56,6 +59,15 @@ class ModuleBaseSpec extends BaseSpec {
.collectEntries { key, value -> [(key.toString()): value.toString()] }
}

protected static Map<String, String> getOptableTargetingSettings(boolean isEnabled = true, Endpoint endpoint = OPENRTB2_AUCTION) {
["hooks.${OPTABLE_TARGETING.code}.enabled": isEnabled as String,
"hooks.modules.${OPTABLE_TARGETING.code}.api-endpoint" : "$networkServiceContainer.rootUri/stored-cache".toString(),
"hooks.modules.${OPTABLE_TARGETING.code}.tenant" : PBSUtils.randomString,
"hooks.modules.${OPTABLE_TARGETING.code}.origin" : PBSUtils.randomString,
"hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, [(PROCESSED_AUCTION_REQUEST): [OPTABLE_TARGETING]]))]
.collectEntries { key, value -> [(key.toString()): value.toString()] }
}

protected static Map<String, String> getOrtb2BlockingSettings(boolean isEnabled = true) {
["hooks.${ORTB2_BLOCKING.code}.enabled": isEnabled as String]
}
Expand Down
Loading