Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Collecting Arbitrary ORTB Parameters #941

Closed
wants to merge 34 commits into from

Conversation

jsligh
Copy link
Collaborator

@jsligh jsligh commented Dec 13, 2023

Closes #877

@jsligh jsligh self-assigned this Dec 13, 2023
@jsligh jsligh linked an issue Dec 13, 2023 that may be closed by this pull request
PrebidMobile/AdUnits/AdUnit.swift Outdated Show resolved Hide resolved
@@ -178,6 +178,7 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest {
nextImp.extPrebid.storedAuctionResponse = Prebid.shared.storedAuctionResponse;
nextImp.extPrebid.isRewardedInventory = self.adConfiguration.adConfiguration.isOptIn;
nextImp.extGPID = self.adConfiguration.gpid;
nextImp.extOrtbObject = self.adConfiguration.ortbObject;
Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI Dec 15, 2023

Choose a reason for hiding this comment

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

Hi @jsligh , @bretg !

I would like to clarify the scope of the implementation to provide the proper review.

For now, I see support only for the imp.ext field. So, the publisher can set the value of this field providing the respective JSON.

However, from my understanding of the feature request, this new API should provide the ability to set any part of the request with the given objects.

For instance, the publisher should be able to set the whole imp object like:

adUnit.setOrtb("
{
 "imp": [
    {
      "id": "1",
      "bidfloor": 0.03,
      "banner": {
        "h": 250,
        "w": 300,
        "pos": 0
      }
    }
  ]
}
")

Or set the app info like:

adUnit.setOrtb("
{
"app": { "id": "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA", "name": "Yahoo Weather", "cat": [ "IAB15", "IAB15-10" ], "ver": "1.0.2", "bundle": "12345", "storeurl": "https://itunes.apple.com/id628677149", "publisher": { "id": "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfTUCV", "name": "yahoo", "domain": "www.yahoo.com" } },
})

Note: these examples are copy-pasts from the Opertb doc just for demo purposes. Don't treat them as an implementation spec.

So, if the request is about the second variant, the implementation should be extended.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right @YuriyVelichkoPI that the goal is for the SDK to be able to supply any arbitrary ORTB parameter.

My suggestion in the original issue was to break this into two parts: the "global" part and the "imp-level" part:

   addOrtbGlobal('{"ext":{"prebid":{"debug":1,"trace":"verbose"}}}');
   adUnit = BannerAdUnit(configId: "my-stored-request", size: adSize, "{instl:1, ext:{ gpid: "222", data: { foo: "bar" }})

I don't see them every wanting to supply the entire imp object, but they need to be able to add any field under imp, not just in imp.ext.

Copy link
Collaborator Author

@jsligh jsligh Dec 15, 2023

Choose a reason for hiding this comment

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

Thanks for the clarification! I will set it to do fields for the whole imp object, not just imp.ext. @bretg should I also support arbitrary parameters in ext as well? because then we'll have to keep track of two separate objects.

Copy link
Contributor

Choose a reason for hiding this comment

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

The global ext object (and any other global params) would be set in a new function, e.g. addOrtbGlobal

@jsligh jsligh marked this pull request as draft January 4, 2024 19:21
let adUnit = AdUnit(configId: "test", size: CGSize.zero, adFormats: [.banner])
adUnit.setGPID(gpid)
adUnit.setGlobalOrtbObject(globalOrtb)
adUnit.setImpOrtbObject(impOrtb)
Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI Jan 11, 2024

Choose a reason for hiding this comment

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

Hi @jsligh, @bretg !

The current implementation looks good, and it can address the needs described in the ticket. But also, it seems too fragile and publisher-dependent in the long-term perspective.

For example, by providing an API to set the Global object and Imp object independently sooner or later, we will get a situation when the publisher mixes these approaches, and the final request will have the wrong structure.

Also, correct me if I am wrong, but from what I see in the PBMORTBAbstractTest.swift, if a publisher provides, for example, the same ext object via the ortbObject and via AdUnit API, the filed will be duplicated in the final bid request. Instead, the data provided in ortbObject should override any other similar data. In the same time, we can't ignore data received from API fields because they can be set by 3p services, like identity providers, so the SDK update may lead to losing data in the bid request silently.

Also, there are no changes for Rendering and Mediation API in this PR.

As I told you from the very beginning, it is an extremely complex task that assumes a lot of design work first. So, instead of searching for implementation gaps, let's design the implementation spec first, and if it covers all the objectives, it is robust for integration and unambiguous for utilization - we'll update the PR accordingly.

I can provide a draft version early next week so we can discuss it.

cc: @alexsavelyev , @mmullin

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the thoughts Yuriy. My take is based on the experience we've had with Prebid.js:

  • ORTB is a standard. Developers have to understand it anyhow in order to debug.
  • Things in our industry change too quickly for an API to keep up
  • Therefore, instead of trying to design a new API function everytime there's a new feature, letting developers have direct access to ORTB is better in every way: smaller API footprint, more robust to continuing evolution, developers have less to learn.

I agree that the "rendering API" also needs this functionality, though as we discussed earlier today, I frankly have issues with a core assumption of that API -- that Prebid can always choose the winner. I'm going to open a separate issue to discuss that.

Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI Jan 11, 2024

Choose a reason for hiding this comment

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

I agree with all points and statements except:

Developers have to understand it anyhow in order to debug.

With full respect, but from all our experience in mobile development on both sides - publishers and the ad tech industry, I should say that developers of mobile apps don't have a knowledge of ORTB.

Open RTB, as usual, is used in s2s cases. In the mobile world, the ad request (SDK --> SSP) has a custom format depending on each particular implementation (GMA SDK, AppLovin SDK, ironSource SDK). App developers, in their own turn, work only with the API of each particular SDK that provides a certain set of parameters to tune. This API and integration documentation is the final frontier of their knowledge of ad tech and what is happening behind the ad requests to the SSP (or other platforms).

Of course, with exceptions when publishers have an AdOps department that directly works on monetization. These guys have a great understanding of the ecosystem.

So, in order to prevent logical errors, it makes sense to restrict the ability to shoot themselves in the foot as much as possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

@bretg @jsligh please review the proposal. For now, it is in the doc so you can easily edit it. Once everything is aligned we can copy the text here:

https://docs.google.com/document/d/1cRWDNO6i7wLyQdQA9ZzmK0QumIr2vvC40r5GhHhOSz4/edit?usp=sharing

Sorry if the intro sections are too long. I tried to highlight the context and pitfalls that impact the proposed design.

cc: @alexsavelyev @mmullin

Copy link
Contributor

@YuriyVelichkoPI YuriyVelichkoPI left a comment

Choose a reason for hiding this comment

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

Except for the requested changes, I want to ask you to redo the branch and make it clean from changes from the master. For now, I see which changes are related to the parent task and which were merged from the master. But later whey can confuse.

The best way to keep the history clear is to follow the trunk-based development methodology: https://trunkbaseddevelopment.com/ as we did in mobile repositories.

It will help any collaborator to see the clear history that is especially important for the open source project.

@@ -387,6 +387,16 @@ public class AdUnit: NSObject, DispatcherDelegate {
return adUnitConfig.gpid
}

// MARK: Global ORTBObject

public func setOrtbConfig(_ ortbObject: [String: Any]?) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The custom request should be set as a String.

Publishers should just put the JSON string as a configuration option and not a dictionary.

@@ -68,6 +68,58 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary {
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 *o = [self.arbitraryJsonConfig mutableCopy];
Copy link
Contributor

Choose a reason for hiding this comment

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

We should avoid such names for variables.

[ret setObject:[dictionary2 objectForKey: key] forKey:key];
}
}
//not sure what to do if array of objects
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure about this comment. Does it mean that the algorithm doesn't merge part of the data?

return ret;
}

- (nonnull PBMMutableJsonDictionary *)mergeDictionaries:(NSMutableDictionary*)dictionary1 joiningArgument2:(NSMutableDictionary*)dictionary2
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, we can merge dictionaries. This path is faster to implement. However, it makes the solution restricted to such things as

  • RTB protocol validation (proper structure, objects, and types)
  • Warnings for objects prohibited from merging.
  • Upgrading protocol version. See the item 1 - the validation issue.

It is not critical, but I would still merge serialized objects. It is a more reliable approach from the support perspective.


pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234", "imp": ["hi": "hello"]]

codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"\(uuid)\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"test\":2,\"tmax\":2000}")
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to cover more use cases here, including incorrect JSONs. Not overriding of reserved objects. Also the custom parameter should be a String but not Dictionary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support collecting arbitrary ORTB parameters
3 participants