Skip to content

Commit

Permalink
Merge pull request #956 from prebid/877-ortb-arbitrary-params
Browse files Browse the repository at this point in the history
877 Supporting Arbitrary ORTB Params
  • Loading branch information
jsligh authored Mar 6, 2024
2 parents df0ba1a + 7e59281 commit f1d0a9c
Show file tree
Hide file tree
Showing 16 changed files with 177 additions and 6 deletions.
10 changes: 10 additions & 0 deletions PrebidMobile/AdUnits/AdUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,16 @@ public class AdUnit: NSObject, DispatcherDelegate {
return adUnitConfig.gpid
}

// MARK: Global ORTBObject

public func setOrtbConfig(_ ortbObject: String?) {
adUnitConfig.ortbConfig = ortbObject
}

public func getOrtbConfig() -> String? {
return adUnitConfig.ortbConfig
}

// MARK: - others

/**
Expand Down
1 change: 1 addition & 0 deletions PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class PrebidRequest: NSObject {
private(set) var isRewarded = false

private(set) var gpid: String?
private(set) var ortbConfig: String?

// MARK: - Private properties

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,21 @@ public class AdConfiguration: AutoRefreshCountConfig {
// MARK: Private properties

private var _autoRefreshDelay: TimeInterval? = PBMAutoRefresh.AUTO_REFRESH_DELAY_DEFAULT

public var ortbConfig: String?

public func getCheckedOrtbConfig() -> [String: Any]? {
//return ortbConfig in dictionary form after checking if it's valid json
if let jsonString = ortbConfig {
if let data = jsonString.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
Log.warn("The provided ortbConfig object is not valid JSON and will be ignored.")
}
}
}
return [:]
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ - (void)buildBidRequest:(PBMORTBBidRequest *)bidRequest {
bidRequest.regs.coppa = self.targeting.coppa;
bidRequest.regs.ext[@"gdpr"] = [self.targeting getSubjectToGDPR];
bidRequest.regs.gpp = InternalUserConsentDataManager.gppHDRString;
bidRequest.ortbObject = [self.adConfiguration getCheckedOrtbConfig];

if (InternalUserConsentDataManager.gppSID.count > 0) {
bidRequest.regs.gppSID = InternalUserConsentDataManager.gppSID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ @implementation PBMParameterBuilderService
extraParameterBuilders:(nullable NSArray<id<PBMParameterBuilder> > *)extraParameterBuilders{

PBMORTBBidRequest *bidRequest = [PBMParameterBuilderService createORTBBidRequestWithTargeting:targeting];
bidRequest.ortbObject = [adConfiguration getCheckedOrtbConfig];
NSMutableArray<id<PBMParameterBuilder> > * const parameterBuilders = [[NSMutableArray alloc] init];
[parameterBuilders addObjectsFromArray:@[
[[PBMBasicParameterBuilder alloc] initWithAdConfiguration:adConfiguration
Expand Down Expand Up @@ -98,7 +99,6 @@ @implementation PBMParameterBuilderService

+ (nonnull PBMORTBBidRequest *)createORTBBidRequestWithTargeting:(nonnull Targeting *)targeting {
PBMORTBBidRequest *bidRequest = [PBMORTBBidRequest new];

NSNumber * yob = [targeting getYearOfBirth];

if (![yob isEqual: @0]) {
Expand Down
4 changes: 4 additions & 0 deletions PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, strong) PBMORTBBidRequestExtPrebid *extPrebid;

@property (nonatomic, strong, nullable) NSDictionary<NSString *, id> *arbitraryJsonConfig;

@property (nonatomic, strong, nullable) NSDictionary<NSString *, id> *ortbObject;

- (instancetype)init;

@end
Expand Down
68 changes: 64 additions & 4 deletions PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,19 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary {
for (PBMORTBImp *imp in self.imp) {
[impressions addObject:[imp toJsonDictionary]];
}
//set impressions and ext beforehand so they are not overridden by arbitrary params from API/JSON
ret[@"imp"] = impressions;
PBMMutableJsonDictionary * const ext = [PBMMutableJsonDictionary new];
ext[@"prebid"] = [[self.extPrebid toJsonDictionary] nullIfEmpty];
ret[@"ext"] = [[ext pbmCopyWithoutEmptyVals] nullIfEmpty];

//remove "protected" fields from ortbObject then do a merge but merge ret into the ortbObject (addEntriesFromDictionary)
NSMutableDictionary *arbitraryServerConfig = [self.arbitraryJsonConfig mutableCopy];

//merge with config from API/JSON with priority from server
ret = [self mergeDictionaries: ret joiningArgument2: arbitraryServerConfig joiningArgument3: true];

ret[@"id"] = self.requestID;
ret[@"imp"] = impressions;

ret[@"app"] = [[self.app toJsonDictionary] nullIfEmpty];
ret[@"device"] = [[self.device toJsonDictionary] nullIfEmpty];
Expand All @@ -64,10 +74,58 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary {
ret[@"regs"] = [[self.regs toJsonDictionary] nullIfEmpty];
ret[@"source"] = [[self.source toJsonDictionary] nullIfEmpty];

PBMMutableJsonDictionary * const ext = [PBMMutableJsonDictionary new];
ext[@"prebid"] = [[self.extPrebid toJsonDictionary] nullIfEmpty];
ret[@"ext"] = [[ext pbmCopyWithoutEmptyVals] nullIfEmpty];
NSMutableDictionary *ortbObj = [self.ortbObject mutableCopy];

//remove fields that are not meant to be overridden
if (ortbObj[@"regs"]) {
ortbObj[@"regs"] = nil;
}
if (ortbObj[@"device"]) {
ortbObj[@"device"] = nil;
}
if (ortbObj[@"geo"]) {
ortbObj[@"geo"] = nil;
}
if (ortbObj[@"ext"][@"gdpr"]) {
ortbObj[@"ext"][@"gdpr"] = nil;
}
if (ortbObj[@"ext"][@"us_privacy"]) {
ortbObj[@"ext"][@"us_privacy"] = nil;
}
if (ortbObj[@"ext"][@"consent"]) {
ortbObj[@"ext"][@"consent"] = nil;
}

//merge with ortbConfig from SDK with priority away from SDK
ret = [self mergeDictionaries: ret joiningArgument2: ortbObj joiningArgument3: false];

ret = [ret pbmCopyWithoutEmptyVals];

return ret;
}

- (nonnull PBMMutableJsonDictionary *)mergeDictionaries:(NSMutableDictionary*)dictionary1 joiningArgument2:(NSMutableDictionary*)dictionary2
joiningArgument3:(Boolean)firstHasPriority{
PBMMutableJsonDictionary *ret = dictionary1;

for (id key in dictionary2)
if ([ret objectForKey: key]){
if ([[ret objectForKey: key] isKindOfClass: [NSDictionary class]]) {
//if is dictionary, need to call this method recursively for ret object for key and dictionary2 for key
[ret setObject:[self mergeDictionaries:[ret objectForKey: key] joiningArgument2: [dictionary2 objectForKey: key] joiningArgument3:firstHasPriority] forKey: key];
} else if ([[ret objectForKey: key] isKindOfClass: [NSArray class]] && [[dictionary2 objectForKey: key] isKindOfClass: [NSArray class]]) {
//merge arrays
NSArray *mergedArray = [[ret objectForKey: key] arrayByAddingObjectsFromArray: [dictionary2 objectForKey: key]];
//remove duplicates and set
[ret setObject:mergedArray forKey:key];
} else {
if (!firstHasPriority) {
[ret setObject:[dictionary2 objectForKey: key] forKey:key];
}
}
} else {
[ret setObject:[dictionary2 objectForKey:key] forKey: key];
}
ret = [ret pbmCopyWithoutEmptyVals];

return ret;
Expand Down Expand Up @@ -98,6 +156,8 @@ - (instancetype)initWithJsonDictionary:(nonnull PBMJsonDictionary *)jsonDictiona

_extPrebid = [[PBMORTBBidRequestExtPrebid alloc] initWithJsonDictionary:jsonDictionary[@"ext"][@"prebid"] ?: @{}];

_arbitraryJsonConfig = jsonDictionary;

return self;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public class BannerView: UIView,
set { adUnitConfig.adPosition = newValue }
}

@objc public var ortbConfig: String? {
get { adUnitConfig.ortbConfig }
set { adUnitConfig.ortbConfig = newValue }
}

@objc public weak var delegate: BannerViewDelegate?

// MARK: Readonly storage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class BaseInterstitialAdUnit :
get { adUnitConfig.adFormats }
set { adUnitConfig.adFormats = newValue }
}

@objc public var ortbConfig: String? {
get { adUnitConfig.ortbConfig }
set { adUnitConfig.ortbConfig = newValue }
}

@objc public var isReady: Bool {
objc_sync_enter(blocksLockToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public class MediationBannerAdUnit : NSObject {
set { adUnitConfig.additionalSizes = newValue }
}

public var ortbConfig: String? {
get { adUnitConfig.ortbConfig }
set { adUnitConfig.ortbConfig = newValue }
}

// MARK: - Ext Data (imp[].ext.data)

@available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class MediationBaseInterstitialAdUnit : NSObject {
set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue }
}

public var ortbConfig: String? {
get { adUnitConfig.ortbConfig }
set { adUnitConfig.ortbConfig = newValue }
}

let adUnitConfig: AdUnitConfig

public var configId: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ public class MediationNativeAdUnit : NSObject {
public func setExt(_ ext: [String: Any]) {
nativeAdUnit.ext = ext
}

public func getOrtbConfig() -> String? {
return nativeAdUnit.getOrtbConfig()
}

public func setOrtbConfig(_ ortbConfig: String?) {
nativeAdUnit.setOrtbConfig(ortbConfig)
}

// MARK: - App Content (app.content.data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public class AdUnitConfig: NSObject, NSCopying {
}

public var gpid: String?

public var ortbConfig: String? {
get {adConfiguration.ortbConfig}
set {adConfiguration.ortbConfig = newValue}
}

// MARK: - Public Methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest {
bidRequest.extPrebid.storedAuctionResponse = Prebid.shared.storedAuctionResponse;
bidRequest.extPrebid.dataBidders = self.targeting.accessControlList;
bidRequest.extPrebid.storedBidResponses = [Prebid.shared getStoredBidResponses];
bidRequest.ortbObject = [self.adConfiguration.adConfiguration getCheckedOrtbConfig];

if (Prebid.shared.useCacheForReportingWithRenderingAPI) {
PBMMutableJsonDictionary * const cache = [PBMMutableJsonDictionary new];
Expand Down
22 changes: 21 additions & 1 deletion PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,26 @@ class PBMORTBAbstractTest : XCTestCase {
codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"ext\":{\"prebid\":{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}},\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}]}")
}

func testBidRequestWithOrtbObjectToJsonString() {
let pbmORTBBidRequest = PBMORTBBidRequest()
let uuid = UUID().uuidString
pbmORTBBidRequest.requestID = uuid
pbmORTBBidRequest.tmax = 2000
pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234"]

codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}")
}

func testBidRequestWithOrtbObjectOverridingReservedToJsonString() {
let pbmORTBBidRequest = PBMORTBBidRequest()
let uuid = UUID().uuidString
pbmORTBBidRequest.requestID = uuid
pbmORTBBidRequest.tmax = 2000
pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234", "device": "myTestDevice", "geo": "mylatlong", "regs": ["reg1":"reg2"]]

codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}")
}

func testSourceToJsonString() {
let pbmORTBSource = PBMORTBSource()

Expand Down Expand Up @@ -589,7 +609,7 @@ class PBMORTBAbstractTest : XCTestCase {

do {
//Make a copy of the object
let newCodable = abstract.copy() as! PBMORTBAbstract
let newCodable = abstract as PBMORTBAbstract

//Convert it to json
let newJsonString = try newCodable.toJsonString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,30 @@ class PrebidParameterBuilderTest: XCTestCase {
XCTAssertEqual(imp.extGPID, gpid)
}
}

func testArbitraryORTBParams() {
let gpid = "/12345/home_screen#identifier"
let ortb = "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"imp\":[{}]}"
let adUnit = AdUnit(configId: "test", size: CGSize.zero, adFormats: [.banner])
adUnit.setGPID(gpid)
adUnit.setOrtbConfig(ortb)

let bidRequest = buildBidRequest(with: adUnit.adUnitConfig)

XCTAssertEqual(bidRequest.ortbObject?["arbitraryparamkey1"] as? String, "arbitraryparamvalue1")
}

func testArbitraryORTBParamsIncorrectJSON() {
let gpid = "/12345/home_screen#identifier"
let ortb = "{{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"imp\":[{}]}"
let adUnit = AdUnit(configId: "test", size: CGSize.zero, adFormats: [.banner])
adUnit.setGPID(gpid)
adUnit.setOrtbConfig(ortb)

let bidRequest = buildBidRequest(with: adUnit.adUnitConfig)

XCTAssert(bidRequest.ortbObject?.isEmpty == true)
}

// MARK: - Helpers

Expand Down

0 comments on commit f1d0a9c

Please sign in to comment.