Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f40ec3c
WURFL-devicedetection module implementation (compatible with PBS Java…
andreacastello Jan 17, 2025
eef4efc
updated README.md
andreacastello Jan 17, 2025
12eb97c
updated README.md title
andreacastello Jan 17, 2025
da8fd51
some README.md wording improvements
andreacastello Jan 17, 2025
c0dd077
used markdown table for config params.
andreacastello Jan 17, 2025
39bf6d7
added maintainer email address
andreacastello Jan 17, 2025
3aff037
modified sample request data
andreacastello Jan 17, 2025
eedc3e0
fixed typos
andreacastello Jan 17, 2025
cac7b71
Merge pull request #1 from WURFL/module-wurfl-devicedetection
andreacastello Jan 17, 2025
0e93e64
improved wording
andreacastello Jan 17, 2025
89cb0dd
improved published specific enrichment paragraph
andreacastello Jan 17, 2025
7a55ba2
better doc formatting
andreacastello Jan 17, 2025
9d95efd
Merged upstream master branch to local master
andreacastello Feb 21, 2025
93f3182
Updated WURFL API version, uncommented module add into modules pom. M…
andreacastello Feb 24, 2025
96d91e4
Added WURFL API mock classes
andreacastello Feb 25, 2025
9b8b10e
checkstyle compliance
andreacastello Feb 25, 2025
1bd2865
Updated README. More checkstyle compliance
andreacastello Feb 25, 2025
cee250a
Removed commented dependency
andreacastello Feb 27, 2025
59fe1a6
Improved WURFL module README.md
andreacastello Mar 3, 2025
c0fe0db
Merge pull request #2 from WURFL/module-wurfl-devicedetection
andreacastello Mar 3, 2025
4d925c1
Merge branch 'prebid:master' into support-bidder-specific-device-data
andreacastello Mar 3, 2025
f5fa9ff
Aligned dev branch to upstream repo master branch
andreacastello Mar 3, 2025
0647294
Merge branch 'prebid:master' into support-bidder-specific-device-data…
andreacastello Mar 4, 2025
b5d502e
PREB-38: initial unit test for Device in bidder config (test fails, n…
andreacastello Mar 5, 2025
6140fd7
PREB-38: FPD device in bidder config impl
andreacastello Mar 6, 2025
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
10 changes: 10 additions & 0 deletions src/main/java/org/prebid/server/auction/ExchangeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.iab.openrtb.request.App;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Content;
import com.iab.openrtb.request.Device;
import com.iab.openrtb.request.Dooh;
import com.iab.openrtb.request.Eid;
import com.iab.openrtb.request.Imp;
Expand Down Expand Up @@ -768,12 +769,15 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
final App app = bidRequest.getApp();
final Site site = bidRequest.getSite();
final Dooh dooh = bidRequest.getDooh();
final Device device = bidRequest.getDevice();
final ObjectNode fpdSite = fpdConfig != null ? fpdConfig.getSite() : null;
final ObjectNode fpdApp = fpdConfig != null ? fpdConfig.getApp() : null;
final ObjectNode fpdDooh = fpdConfig != null ? fpdConfig.getDooh() : null;
final ObjectNode fpdDevice = fpdConfig != null ? fpdConfig.getDevice() : null;
final App preparedApp = prepareApp(app, fpdApp, useFirstPartyData);
final Site preparedSite = prepareSite(site, fpdSite, useFirstPartyData);
final Dooh preparedDooh = prepareDooh(dooh, fpdDooh, useFirstPartyData);
final Device preparedDevice = prepareDevice(device, fpdDevice, useFirstPartyData);

final List<String> distributionChannels = new ArrayList<>();
Optional.ofNullable(preparedApp).ifPresent(ignored -> distributionChannels.add("app"));
Expand All @@ -800,6 +804,7 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
final boolean isApp = preparedApp != null;
final boolean isDooh = !isApp && preparedDooh != null;
final boolean isSite = !isApp && !isDooh && preparedSite != null;
final boolean isDevice = preparedDevice != null;

final List<Imp> preparedImps = prepareImps(
bidder,
Expand All @@ -818,6 +823,7 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult,
.app(isApp ? preparedApp : null)
.dooh(isDooh ? preparedDooh : null)
.site(isSite ? preparedSite : null)
.device(isDevice ? preparedDevice : null)
.source(prepareSource(bidder, bidRequest, transmitTid))
.ext(prepareExt(bidder, bidderToPrebidBidders, bidderToMultiBid, bidRequest.getExt()))
.build();
Expand Down Expand Up @@ -932,6 +938,10 @@ private App prepareApp(App app, ObjectNode fpdApp, boolean useFirstPartyData) {
return useFirstPartyData ? fpdResolver.resolveApp(maskedApp, fpdApp) : maskedApp;
}

private Device prepareDevice(Device device, ObjectNode fpdDevice, boolean useFirstPartyData) {
return useFirstPartyData ? fpdResolver.resolveDevice(device, fpdDevice) : device;
}

private static ExtApp maskExtApp(ExtApp appExt) {
final ExtApp maskedExtApp = ExtApp.of(appExt.getPrebid(), null);
return maskedExtApp.isEmpty() ? null : maskedExtApp;
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/prebid/server/auction/FpdResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.App;
import com.iab.openrtb.request.Device;
import com.iab.openrtb.request.Dooh;
import com.iab.openrtb.request.Site;
import com.iab.openrtb.request.User;
Expand All @@ -23,7 +24,8 @@ public class FpdResolver {
private static final String BIDDERS = "bidders";
private static final String APP = "app";
private static final String DOOH = "dooh";
private static final Set<String> KNOWN_FPD_ATTRIBUTES = Set.of(USER, SITE, APP, DOOH, BIDDERS);
private static final String DEVICE = "device";
private static final Set<String> KNOWN_FPD_ATTRIBUTES = Set.of(USER, SITE, APP, DOOH, DEVICE, BIDDERS);
private static final String CONTEXT = "context";
private static final String DATA = "data";

Expand All @@ -43,6 +45,10 @@ public App resolveApp(App originApp, ObjectNode fpdApp) {
return mergeFpd(originApp, fpdApp, App.class);
}

public Device resolveDevice(Device originDevice, ObjectNode fpdDevice) {
return mergeFpd(originDevice, fpdDevice, Device.class);
}

public Site resolveSite(Site originSite, ObjectNode fpdSite) {
return mergeFpd(originSite, fpdSite, Site.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ public class ExtBidderConfigOrtb {
* Defines the contract for bidrequest.ext.prebid.bidderconfig.config.ortb2.user
*/
ObjectNode user;

/**
* Defines the contract for bidrequest.ext.prebid.bidderconfig.config.ortb2.device
*/
ObjectNode device;
}
64 changes: 56 additions & 8 deletions src/test/java/org/prebid/server/auction/ExchangeServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2679,12 +2679,12 @@ public void shouldUseConcreteOverGeneralSiteWithExtPrebidBidderConfigIgnoringCas

final ObjectNode siteWithPage = mapper.valueToTree(Site.builder().page("testPage").build());
final ExtBidderConfig extBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(siteWithPage, null, null, null));
ExtBidderConfigOrtb.of(siteWithPage, null, null, null, null));
final ExtRequestPrebidBidderConfig concreteFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("SoMeBiDdEr"), extBidderConfig);
final ObjectNode siteWithDomain = mapper.valueToTree(Site.builder().domain("notUsed").build());
final ExtBidderConfig allExtBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(siteWithDomain, null, null, null));
ExtBidderConfigOrtb.of(siteWithDomain, null, null, null, null));
final ExtRequestPrebidBidderConfig allFpdConfig = ExtRequestPrebidBidderConfig.of(singletonList("*"),
allExtBidderConfig);

Expand Down Expand Up @@ -2726,12 +2726,12 @@ public void shouldUseConcreteOverGeneralDoohWithExtPrebidBidderConfig() {

final ObjectNode doohWithVenueType = mapper.valueToTree(Dooh.builder().venuetype(List.of("venuetype")).build());
final ExtBidderConfig extBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, doohWithVenueType, null));
ExtBidderConfigOrtb.of(null, null, doohWithVenueType, null, null));
final ExtRequestPrebidBidderConfig concreteFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("someBidder"), extBidderConfig);
final ObjectNode doohWithDomain = mapper.valueToTree(Dooh.builder().domain("notUsed").build());
final ExtBidderConfig allExtBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, doohWithDomain, null));
ExtBidderConfigOrtb.of(null, null, doohWithDomain, null, null));
final ExtRequestPrebidBidderConfig allFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("*"),
allExtBidderConfig);
Expand Down Expand Up @@ -2775,15 +2775,15 @@ public void shouldUseConcreteOverGeneralAppWithExtPrebidBidderConfigIgnoringCase
final Publisher publisherWithId = Publisher.builder().id("testId").build();
final ObjectNode appWithPublisherId = mapper.valueToTree(App.builder().publisher(publisherWithId).build());
final ExtBidderConfig extBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, appWithPublisherId, null, null));
ExtBidderConfigOrtb.of(null, appWithPublisherId, null, null, null));
final ExtRequestPrebidBidderConfig concreteFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("SoMeBiDdEr"), extBidderConfig);

final Publisher publisherWithIdAndDomain = Publisher.builder().id("notUsed").domain("notUsed").build();
final ObjectNode appWithUpdatedPublisher = mapper.valueToTree(
App.builder().publisher(publisherWithIdAndDomain).build());
final ExtBidderConfig allExtBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, appWithUpdatedPublisher, null, null));
ExtBidderConfigOrtb.of(null, appWithUpdatedPublisher, null, null, null));
final ExtRequestPrebidBidderConfig allFpdConfig = ExtRequestPrebidBidderConfig.of(singletonList("*"),
allExtBidderConfig);

Expand Down Expand Up @@ -2815,20 +2815,68 @@ public void shouldUseConcreteOverGeneralAppWithExtPrebidBidderConfigIgnoringCase
.containsOnly(mergedApp);
}

@Test
public void shouldUseConcreteOverGeneralDeviceWithExtPrebidBidderConfig() {
// given
final Bidder<?> bidder = mock(Bidder.class);
givenBidder("someBidder", bidder, givenEmptySeatBid());

final ObjectNode deviceWithMakeAndModel = mapper.valueToTree(
Device.builder().make("TestMake_001").model("TestModel_001").build());

final ExtBidderConfig extBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, null, null, deviceWithMakeAndModel));
final ExtRequestPrebidBidderConfig concreteFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("SoMeBiDdEr"), extBidderConfig);

final ObjectNode deviceWithUpdatedMakeAndModel = mapper.valueToTree(
Device.builder().make("TestMake_002").model("TestModel_002").build());
final ExtBidderConfig allExtBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, null, null, deviceWithUpdatedMakeAndModel));
final ExtRequestPrebidBidderConfig allFpdConfig = ExtRequestPrebidBidderConfig.of(singletonList("*"),
allExtBidderConfig);

final Device requestDevice = Device.builder().build();

final ExtRequestPrebid extRequestPrebid = ExtRequestPrebid.builder()
.bidderconfig(asList(allFpdConfig, concreteFpdConfig))
.build();
final BidRequest bidRequest = givenBidRequest(givenSingleImp(singletonMap("someBidder", 1)),
builder -> builder.device(requestDevice).ext(ExtRequest.of(extRequestPrebid)));
final Device mergedDevice = Device.builder()
.make("TestMake_001").model("TestModel_001").build();

given(fpdResolver.resolveDevice(any(), any())).willReturn(mergedDevice);

// when
target.holdAuction(givenRequestContext(bidRequest));

// then
final ArgumentCaptor<BidderRequest> bidderRequestCaptor = ArgumentCaptor.forClass(BidderRequest.class);
verify(httpBidderRequester)
.requestBids(any(), bidderRequestCaptor.capture(), any(), any(), any(), any(), anyBoolean());
final List<BidderRequest> capturedBidRequests = bidderRequestCaptor.getAllValues();

assertThat(capturedBidRequests)
.extracting(BidderRequest::getBidRequest)
.extracting(BidRequest::getDevice)
.containsOnly(mergedDevice);
}

@Test
public void shouldUseConcreteOverGeneralUserWithExtPrebidBidderConfig() {
// given
final Bidder<?> bidder = mock(Bidder.class);
givenBidder("someBidder", bidder, givenEmptySeatBid());
final ObjectNode bidderConfigUser = mapper.valueToTree(User.builder().id("userFromConfig").build());
final ExtBidderConfig extBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, null, bidderConfigUser));
ExtBidderConfigOrtb.of(null, null, null, bidderConfigUser, null));
final ExtRequestPrebidBidderConfig concreteFpdConfig = ExtRequestPrebidBidderConfig.of(
singletonList("SomMeBiDdEr"), extBidderConfig);

final ObjectNode emptyUser = mapper.valueToTree(User.builder().build());
final ExtBidderConfig allExtBidderConfig = ExtBidderConfig.of(
ExtBidderConfigOrtb.of(null, null, null, emptyUser));
ExtBidderConfigOrtb.of(null, null, null, emptyUser, null));
final ExtRequestPrebidBidderConfig allFpdConfig = ExtRequestPrebidBidderConfig.of(singletonList("*"),
allExtBidderConfig);
final User requestUser = User.builder().id("erased").buyeruid("testBuyerId").build();
Expand Down
12 changes: 9 additions & 3 deletions src/test/java/org/prebid/server/json/JsonMergerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.App;
import com.iab.openrtb.request.Device;
import com.iab.openrtb.request.Dooh;
import com.iab.openrtb.request.Publisher;
import com.iab.openrtb.request.Site;
Expand Down Expand Up @@ -30,19 +31,22 @@ public void mergeShouldReturnMergedObject() {
final Publisher publisherWithId = Publisher.builder().id("testId").build();
final ObjectNode appWithPublisherId = mapper.valueToTree(App.builder().publisher(publisherWithId).build());
final ObjectNode doohWithVenueType = mapper.valueToTree(Dooh.builder().venuetype(List.of("venuetype")).build());
final ObjectNode deviceWithDeviceType = mapper.valueToTree(Device.builder().devicetype(2).build());
final ExtBidderConfigOrtb firstBidderConfigFpd = ExtBidderConfigOrtb.of(
siteWithPage,
appWithPublisherId,
doohWithVenueType,
null);
null,
deviceWithDeviceType);

final ObjectNode siteWithDomain = mapper.valueToTree(Site.builder().domain("testDomain").build());
final Publisher publisherWithIdAndDomain = Publisher.builder().id("shouldNotBe").domain("domain").build();
final ObjectNode appWithUpdatedPublisher = mapper.valueToTree(App.builder()
.publisher(publisherWithIdAndDomain).build());
final ObjectNode doohWithVenueTypeTax = mapper.valueToTree(Dooh.builder().venuetypetax(3).build());
final ExtBidderConfigOrtb secondBidderConfigFpd =
ExtBidderConfigOrtb.of(siteWithDomain, appWithUpdatedPublisher, doohWithVenueTypeTax, null);
ExtBidderConfigOrtb.of(siteWithDomain, appWithUpdatedPublisher, doohWithVenueTypeTax, null,
deviceWithDeviceType);

// when
final ExtBidderConfigOrtb result = target.merge(
Expand All @@ -56,7 +60,9 @@ public void mergeShouldReturnMergedObject() {
final ObjectNode mergedApp = mapper.valueToTree(App.builder().publisher(mergedPublisher).build());
final ObjectNode mergedDooh = mapper.valueToTree(
Dooh.builder().venuetype(List.of("venuetype")).venuetypetax(3).build());
final ExtBidderConfigOrtb mergedConfigFpd = ExtBidderConfigOrtb.of(mergedSite, mergedApp, mergedDooh, null);
final ObjectNode mergedDevice = mapper.valueToTree(Device.builder().devicetype(2).build());
final ExtBidderConfigOrtb mergedConfigFpd = ExtBidderConfigOrtb.of(mergedSite, mergedApp, mergedDooh, null,
mergedDevice);

assertThat(result).isEqualTo(mergedConfigFpd);
}
Expand Down