diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java index ea86a7df2..d63696c5e 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java @@ -200,20 +200,8 @@ public void fetchDemand(OnFetchDemandResult listener) { allowNullableAdObject = true; fetchDemand(null, resultCode -> { - BidInfo bidInfo; - if (bidResponse != null) { - bidInfo = new BidInfo(resultCode, bidResponse.getTargeting()); - if (resultCode == ResultCode.SUCCESS) { - boolean isNative = configuration.getNativeConfiguration() != null; - if (isNative) { - String cacheId = CacheManager.save(bidResponse.getWinningBidJson()); - Util.saveCacheId(cacheId, adObject); - bidInfo.setNativeResult(cacheId, bidResponse.getExpirationTimeSeconds()); - } - } - } else { - bidInfo = new BidInfo(resultCode, null); - } + BidInfo bidInfo = BidInfo.create(resultCode, bidResponse, configuration); + Util.saveCacheId(bidInfo.getNativeCacheId(), adObject); listener.onComplete(bidInfo); }); } @@ -406,6 +394,11 @@ public void setPbAdSlot(String pbAdSlot) { configuration.setPbAdSlot(pbAdSlot); } + @Nullable + public String getGpid() { + return configuration.getGpid(); + } + public void setGpid(@Nullable String gpid) { configuration.setGpid(gpid); } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/data/BidInfo.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/data/BidInfo.java index a05309bdf..4a1f6be3a 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/data/BidInfo.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/data/BidInfo.java @@ -3,35 +3,39 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.prebid.mobile.CacheManager; import org.prebid.mobile.ResultCode; +import org.prebid.mobile.configuration.AdUnitConfiguration; +import org.prebid.mobile.rendering.bidding.data.bid.Bid; +import org.prebid.mobile.rendering.bidding.data.bid.BidResponse; import java.util.Map; public class BidInfo { @NonNull - private ResultCode resultCode; + private final ResultCode resultCode; @Nullable private Map targetingKeywords; @Nullable + private Map events; + @Nullable private String nativeCacheId; @Nullable private Integer exp; - public BidInfo( - @NonNull ResultCode resultCode, - @Nullable Map targetingKeywords - ) { - this.resultCode = resultCode; - this.targetingKeywords = targetingKeywords; - } + /** + * Key for {@link #getEvents()} map to get win event. + */ + public static final String EVENT_WIN = "ext.prebid.events.win"; + /** + * Key for {@link #getEvents()} map to get impression event. + */ + public static final String EVENT_IMP = "ext.prebid.events.imp"; - public void setNativeResult( - String nativeCacheId, - Integer expirationTimeSeconds - ) { - this.nativeCacheId = nativeCacheId; - this.exp = expirationTimeSeconds; + + private BidInfo(@NonNull ResultCode resultCode) { + this.resultCode = resultCode; } @NonNull @@ -54,4 +58,38 @@ public Integer getExp() { return exp; } + @Nullable + public Map getEvents() { + return events; + } + + + @NonNull + public static BidInfo create( + @NonNull ResultCode resultCode, + @Nullable BidResponse bidResponse, + @Nullable AdUnitConfiguration configuration + ) { + BidInfo bidInfo = new BidInfo(resultCode); + if (bidResponse == null) { + return bidInfo; + } + + bidInfo.targetingKeywords = bidResponse.getTargeting(); + + bidInfo.exp = bidResponse.getExpirationTimeSeconds(); + + Bid winningBid = bidResponse.getWinningBid(); + if (winningBid != null) { + bidInfo.events = winningBid.getEvents(); + } + + boolean isNative = configuration != null && configuration.getNativeConfiguration() != null; + if (isNative && bidInfo.resultCode == ResultCode.SUCCESS) { + bidInfo.nativeCacheId = CacheManager.save(bidResponse.getWinningBidJson()); + } + + return bidInfo; + } + } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/MultiformatAdUnitFacade.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/MultiformatAdUnitFacade.java index 21706db61..35c4efe8f 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/MultiformatAdUnitFacade.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/MultiformatAdUnitFacade.java @@ -1,5 +1,7 @@ package org.prebid.mobile.api.original; +import android.annotation.SuppressLint; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,6 +18,7 @@ import org.prebid.mobile.VideoParameters; import org.prebid.mobile.api.data.AdFormat; import org.prebid.mobile.api.exceptions.AdException; +import org.prebid.mobile.configuration.AdUnitConfiguration; import org.prebid.mobile.configuration.NativeAdUnitConfiguration; import org.prebid.mobile.rendering.bidding.data.bid.BidResponse; import org.prebid.mobile.rendering.bidding.listeners.BidRequesterListener; @@ -135,4 +138,10 @@ public BidResponse getBidResponse() { return bidResponse; } + @SuppressLint("VisibleForTests") + @Override + public AdUnitConfiguration getConfiguration() { + return super.getConfiguration(); + } + } \ No newline at end of file diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/OnCompleteListenerImpl.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/OnCompleteListenerImpl.java index 2d44f122b..075cd347c 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/OnCompleteListenerImpl.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/OnCompleteListenerImpl.java @@ -3,13 +3,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.prebid.mobile.CacheManager; import org.prebid.mobile.OnCompleteListener; import org.prebid.mobile.OnCompleteListener2; import org.prebid.mobile.ResultCode; import org.prebid.mobile.Util; import org.prebid.mobile.api.data.BidInfo; -import org.prebid.mobile.rendering.bidding.data.bid.BidResponse; import java.util.Map; @@ -19,23 +17,19 @@ class OnCompleteListenerImpl implements OnCompleteListener, OnCompleteListener2 { @Nullable - private Object adObject; + private final Object adObject; @NonNull - private PrebidRequest request; + private final MultiformatAdUnitFacade adUnit; @NonNull - private MultiformatAdUnitFacade adUnit; - @NonNull - private OnFetchDemandResult listener; + private final OnFetchDemandResult listener; OnCompleteListenerImpl( @NonNull MultiformatAdUnitFacade adUnit, - @NonNull PrebidRequest request, @Nullable Object adObject, @NonNull OnFetchDemandResult listener ) { this.adObject = adObject; this.adUnit = adUnit; - this.request = request; this.listener = listener; } @@ -51,31 +45,9 @@ public void onComplete(ResultCode resultCode, @Nullable Map unmo private void notifyListener(ResultCode resultCode) { - BidResponse bidResponse = adUnit.getBidResponse(); - - if (bidResponse == null) { - listener.onComplete(new BidInfo(resultCode, null)); - return; - } - - BidInfo bidInfo = new BidInfo(resultCode, bidResponse.getTargeting()); - saveCacheForNativeIfNeeded(bidResponse, bidInfo, resultCode); + BidInfo bidInfo = BidInfo.create(resultCode, adUnit.getBidResponse(), adUnit.getConfiguration()); + Util.saveCacheId(bidInfo.getNativeCacheId(), adObject); listener.onComplete(bidInfo); } - private void saveCacheForNativeIfNeeded( - BidResponse bidResponse, - BidInfo bidInfo, - ResultCode resultCode - ) { - if (resultCode == ResultCode.SUCCESS) { - boolean isNative = request.getNativeParameters() != null; - if (isNative) { - String cacheId = CacheManager.save(bidResponse.getWinningBidJson()); - Util.saveCacheId(cacheId, adObject); - bidInfo.setNativeResult(cacheId, bidResponse.getExpirationTimeSeconds()); - } - } - } - } \ No newline at end of file diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/PrebidAdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/PrebidAdUnit.java index 12681b0e6..a162809de 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/PrebidAdUnit.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/original/PrebidAdUnit.java @@ -75,7 +75,7 @@ private void baseFetchDemand( } if (request == null || requestDoesNotHaveAnyConfiguration(request)) { - userListener.onComplete(new BidInfo(ResultCode.INVALID_PREBID_REQUEST_OBJECT, null)); + userListener.onComplete(BidInfo.create(ResultCode.INVALID_PREBID_REQUEST_OBJECT, null, null)); return; } @@ -85,7 +85,7 @@ private void baseFetchDemand( adUnit = new MultiformatAdUnitFacade(configId, request); - OnCompleteListenerImpl innerListener = new OnCompleteListenerImpl(adUnit, request, adObject, userListener); + OnCompleteListenerImpl innerListener = new OnCompleteListenerImpl(adUnit, request, userListener); if (adObject != null) { adUnit.fetchDemand(adObject, innerListener); } else { diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java index 07e354a5d..df727a7b5 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java @@ -18,8 +18,12 @@ import android.util.Base64; + +import androidx.annotation.Nullable; + import org.json.JSONArray; import org.json.JSONObject; +import org.prebid.mobile.api.data.BidInfo; import org.prebid.mobile.rendering.models.internal.MacrosModel; import org.prebid.mobile.rendering.models.openrtb.bidRequests.MobileSdkPassThrough; import org.prebid.mobile.rendering.utils.helpers.MacrosResolutionHelper; @@ -126,6 +130,9 @@ public class Bid { // wait between the auction and the actual impression private int exp; + @Nullable + private Map events; + private MobileSdkPassThrough mobileSdkPassThrough; protected Bid() { @@ -250,6 +257,11 @@ public MobileSdkPassThrough getMobileSdkPassThrough() { return mobileSdkPassThrough; } + @Nullable + public Map getEvents() { + return events; + } + public static Bid fromJSONObject(JSONObject jsonObject) { Bid bid = new Bid(); if (jsonObject == null) { @@ -286,7 +298,9 @@ public static Bid fromJSONObject(JSONObject jsonObject) { JSONObject ext = jsonObject.optJSONObject("ext"); if (ext != null) { - bid.prebid = Prebid.fromJSONObject(ext.optJSONObject("prebid")); + Prebid prebidObject = Prebid.fromJSONObject(ext.optJSONObject("prebid")); + setEvents(bid, prebidObject); + bid.prebid = prebidObject; bid.mobileSdkPassThrough = MobileSdkPassThrough.create(ext); } @@ -341,4 +355,21 @@ private static void substituteMacros(Bid bid) { bid.adm = MacrosResolutionHelper.resolveAuctionMacros(bid.adm, macrosModelMap); bid.nurl = MacrosResolutionHelper.resolveAuctionMacros(bid.nurl, macrosModelMap); } + + + private static void setEvents(Bid bid, Prebid prebidObject) { + HashMap events = new HashMap<>(); + String winUrl = prebidObject.getWinEventUrl(); + if (winUrl != null) { + events.put(BidInfo.EVENT_WIN, winUrl); + } + String impUrl = prebidObject.getImpEventUrl(); + if (impUrl != null) { + events.put(BidInfo.EVENT_IMP, impUrl); + } + if (!events.isEmpty()) { + bid.events = events; + } + } + } diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/original/OnCompleteListenerImplTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/original/OnCompleteListenerImplTest.java index a1665ab28..747abc222 100644 --- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/original/OnCompleteListenerImplTest.java +++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/original/OnCompleteListenerImplTest.java @@ -16,6 +16,7 @@ import org.prebid.mobile.NativeParameters; import org.prebid.mobile.ResultCode; import org.prebid.mobile.api.data.BidInfo; +import org.prebid.mobile.rendering.bidding.data.bid.Bid; import org.prebid.mobile.rendering.bidding.data.bid.BidResponse; import java.util.HashMap; @@ -48,7 +49,6 @@ public void destroy() throws Exception { public void emptyBidResponse() { OnCompleteListenerImpl subject = new OnCompleteListenerImpl( mockAdUnit, - mockPrebidRequest, null, mockListener ); @@ -66,13 +66,13 @@ public void emptyBidResponse() { assertNull(bidInfo.getNativeCacheId()); assertNull(bidInfo.getExp()); assertNull(bidInfo.getTargetingKeywords()); + assertNull(bidInfo.getEvents()); } @Test public void fullBidResponse() { OnCompleteListenerImpl subject = new OnCompleteListenerImpl( mockAdUnit, - mockPrebidRequest, null, mockListener ); @@ -81,6 +81,7 @@ public void fullBidResponse() { keywords.put("key1", "value1"); keywords.put("key2", "value2"); when(mockBidResponse.getTargeting()).thenReturn(keywords); + when(mockBidResponse.getExpirationTimeSeconds()).thenReturn(null); when(mockAdUnit.getBidResponse()).thenReturn(mockBidResponse); subject.onComplete(ResultCode.SUCCESS); @@ -94,13 +95,13 @@ public void fullBidResponse() { assertEquals(keywords, bidInfo.getTargetingKeywords()); assertNull(bidInfo.getNativeCacheId()); assertNull(bidInfo.getExp()); + assertNull(bidInfo.getEvents()); } @Test public void fullBidResponseWithNative() { OnCompleteListenerImpl subject = new OnCompleteListenerImpl( mockAdUnit, - mockPrebidRequest, null, mockListener ); @@ -108,10 +109,17 @@ public void fullBidResponseWithNative() { HashMap keywords = new HashMap<>(); keywords.put("key1", "value1"); keywords.put("key2", "value2"); + HashMap events = new HashMap<>(); + keywords.put("event1", "url1"); + keywords.put("event2", "url2"); + Bid mockBid = mock(Bid.class); + when(mockBid.getEvents()).thenReturn(events); + when(mockPrebidRequest.getNativeParameters()).thenReturn(mock(NativeParameters.class)); when(mockBidResponse.getTargeting()).thenReturn(keywords); when(mockAdUnit.getBidResponse()).thenReturn(mockBidResponse); when(mockBidResponse.getExpirationTimeSeconds()).thenReturn(300); + when(mockBidResponse.getWinningBid()).thenReturn(mockBid); subject.onComplete(ResultCode.SUCCESS); @@ -122,6 +130,7 @@ public void fullBidResponseWithNative() { assertNotNull(bidInfo); assertEquals(ResultCode.SUCCESS, bidInfo.getResultCode()); assertEquals(keywords, bidInfo.getTargetingKeywords()); + assertEquals(events, bidInfo.getEvents()); assertEquals(Integer.valueOf(300), bidInfo.getExp()); } diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/bidding/data/bid/BidTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/bidding/data/bid/BidTest.java index 4b58e04f3..1c4af32fe 100644 --- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/bidding/data/bid/BidTest.java +++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/bidding/data/bid/BidTest.java @@ -16,16 +16,21 @@ package org.prebid.mobile.rendering.bidding.data.bid; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import org.json.JSONException; import org.json.JSONObject; import org.junit.Test; import org.junit.runner.RunWith; +import org.prebid.mobile.api.data.BidInfo; import org.prebid.mobile.test.utils.ResourceUtils; import org.robolectric.RobolectricTestRunner; import java.io.IOException; - -import static org.junit.Assert.*; +import java.util.Map; @RunWith(RobolectricTestRunner.class) public class BidTest { @@ -37,6 +42,7 @@ public void whenFromJSONObjectAndJSONObjectPassed_ReturnParsedBid() Bid bid = Bid.fromJSONObject(jsonBid); assertEquals("adm", bid.getAdm()); assertEquals("nurl", bid.getNurl()); + assertNull(bid.getEvents()); verifyBid(bid); } @@ -51,6 +57,23 @@ public void whenFromJSONObject_JSONObjectContainingMacrosPassed_ReturnParsedBidW verifyBid(bid); } + @Test + public void events_eventListContainsWinEvent() throws Exception { + JSONObject jsonBid = new JSONObject(ResourceUtils.convertResourceToString("bidding_bid_obj_events.json")); + Bid bid = Bid.fromJSONObject(jsonBid); + + Map events = bid.getEvents(); + assertEquals(2, events.size()); + + String value = events.get(BidInfo.EVENT_WIN); + assertEquals(value, "https://win.com"); + + value = events.get(BidInfo.EVENT_IMP); + assertEquals(value, "https://imp.com"); + + verifyBid(bid); + } + @Test public void whenFromJSONObjectAndNullPassed_ReturnNotNull() { assertNotNull(Bid.fromJSONObject(null)); diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/bidding_bid_obj_events.json b/PrebidMobile/PrebidMobile-core/src/test/resources/bidding_bid_obj_events.json new file mode 100644 index 000000000..699aaa9b1 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/test/resources/bidding_bid_obj_events.json @@ -0,0 +1,59 @@ +{ + "id": "bidId", + "impid": "impId", + "price": 0.15, + "adm": "adm", + "crid": "crid", + "nurl": "nurl", + "burl": "burl", + "lurl": "lurl", + "adid": "adid", + "adomain": [ + "domain1", + "domain2" + ], + "bundle": "bundle", + "iurl": "iurl", + "cid": "cid", + "tactic": "tactic", + "cat": [ + "cat1", + "cat2" + ], + "attr": [ + 1, + 2 + ], + "api": 111, + "protocol": 112, + "qagmediarating": 113, + "language": "language", + "dealid": "dealid", + "w": 320, + "h": 50, + "wratio": 114, + "hratio": 115, + "exp": 116, + "ext": { + "prebid": { + "events": { + "win": "https://win.com", + "imp": "https://imp.com" + }, + "cache": { + "key": "cacheKey", + "url": "cacheUrl", + "bids": { + "url": "bidsUrl", + "cacheId": "bidsCacheId" + } + }, + "targeting": { + "key1": "value1", + "key2": "value2", + "key3": "value3" + }, + "type": "type" + } + } +} \ No newline at end of file