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

Add Greenbids Analytics Adapter #3096

Merged

Conversation

EvgeniiMunin
Copy link
Contributor

@EvgeniiMunin EvgeniiMunin commented Apr 4, 2024

We want to add the Greenbids Analytics Adapter to transfer the payload to Greenbids analytics server a.greenbids.ai according to the following schema. The payload is gathered when invoking the auction context from the PBS endpoints openrtb2/auction and openrtb2/amp.

{
    "version": "2.0.0",
    "auctionId": "21093844-8b8e-482d-9e58-944cc2dcb46c",
    "referrer": "http://localhost:3000/test-prebid%202",
    "sampling": 0.5,
    "prebid": "8.26.0-pre",
    "greenbidsId": "55113654-3e14-402c-b8aa-9a0ff0460a49",
    "pbuid": "greenbids-pbuid",
    "billingId": "74ccf435-3a94-471b-9dbb-73e317bd5a47",
    "adUnits": [
        {
            "code": "/19968336/header-bid-tag-0",
            "mediaTypes": {
                "banner": {
                    "sizes": [
                        [
                            300,
                            250
                        ],
                        [
                            300,
                            600
                        ]
                    ]
                }
            },
            "ortb2Imp": {
                "ext": {
                    "greenbids": {
                        "fingerprint": "55113654-3e14-402c-b8aa-9a0ff0460a49",
                        "keptInAuction": {
                            "appnexus": true
                        },
                        "isExploration": true
                    },
                    "tid": "9c196de8-7e5a-4b90-8a27-5ab558479f2e"
                }
            },
            "bidders": [
                {
                    "bidder": "appnexus",
                    "isTimeout": false,
                    "hasBid": false
                }
            ]
        },
        {
            "code": "/19968336/header-bid-tag-1",
            "mediaTypes": {
                "banner": {
                    "sizes": [
                        [
                            300,
                            250
                        ],
                        [
                            300,
                            600
                        ]
                    ]
                },
                "video": {
                    "context": "instream",
                    "mimes": [
                        "video/mp4"
                    ],
                    "playerSize": [
                        [
                            640,
                            480
                        ]
                    ],
                    "skip": 1,
                    "protocols": [
                        1,
                        2,
                        3,
                        4
                    ]
                }
            },
            "ortb2Imp": {
                "ext": {
                    "greenbids": {
                        "fingerprint": "55113654-3e14-402c-b8aa-9a0ff0460a49",
                        "keptInAuction": {
                            "adyoulike": true,
                            "appnexus": true
                        },
                        "isExploration": true
                    },
                    "tid": "e202c36b-ffd3-4365-bb4e-de27ee9b89eb"
                }
            },
            "bidders": [
                {
                    "bidder": "adyoulike",
                    "isTimeout": false,
                    "hasBid": false
                },
                {
                    "bidder": "appnexus",
                    "isTimeout": false,
                    "hasBid": false
                }
            ]
        }
    ],
    "auctionElapsed": 132
}

The example of the test payload of the given adapter is as follows

{
  "version": "2.2.0",
  "auctionId": "1",
  "referrer": "http://example.com/prebid_server_test.html",
  "sampling": 0.9,
  "prebid": "$prebid.version$",
  "greenbidsId": "accb4deb-0325-48d8-9e43-616db175b52f",
  "pbuid": "PBUID_FROM_GREENBIDS",
  "billingId": "ad6aa370-40f4-44a0-93cb-3a407f2ccb02",
  "adUnits": [
    {
      "code": "a",
      "mediaTypes": {
        "banner": {
          "sizes": [
            [
              320,
              50
            ],
            [
              320,
              50
            ]
          ],
          "pos": null,
          "name": null
        },
        "video": null,
        "native": null
      },
      "bidders": [
        {
          "bidder": "rubicon",
          "isTimeout": false,
          "hasBid": true
        },
        {
          "bidder": "rubicon",
          "isTimeout": false,
          "hasBid": false
        }
      ]
    }
  ],
  "auctionElapsed": 0
}

@EvgeniiMunin EvgeniiMunin changed the title GreenBids Analytics Adapter Greenbids Analytics Adapter Apr 4, 2024
@EvgeniiMunin EvgeniiMunin changed the title Greenbids Analytics Adapter Add Greenbids Analytics Adapter Apr 4, 2024
@EvgeniiMunin EvgeniiMunin marked this pull request as ready for review May 2, 2024 09:34
this.auctionId = auctionContext.getBidRequest().getId();
this.referrer = auctionContext.getBidRequest().getSite().getPage();
this.sampling = greenbidsConfig.getGreenbidsSampling();
this.prebid = "$prebid.version$"; // TODO: to fix
Copy link
Contributor Author

Choose a reason for hiding this comment

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

self-review: currently searching where PBS version field could be extracted from to the payload

.filter(seatNonBid -> !seatNonBid.getNonBid().isEmpty())
.toList();

final Map<String, NonBid> seatsWithNonBids = Optional.of(seatNonBids)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

self-review: When I’ve been testing the AuctionContext.bidRejectionTrackers sending the request for storedresponse for the Rubicon bidder I get both

  • seatsWithNonBids: {rubicon=NonBid(impId=a, statusCode=NO_BID)} extracted from AuctionContext.bidRejectionTrackers
  • seatsWithBids: {rubicon=Bid(id=1, impid=a, price=1.23)} extracted from AuctionContext.BidResponse.seatBid
    is it possible to have 2 responses for the one ad unit from the same bidder in both seat no bid and seat bid or maybe I’m missing something?

Copy link
Collaborator

@AntoxaAntoxic AntoxaAntoxic left a comment

Choose a reason for hiding this comment

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

Please fix the comments first, then I'll continue with the review

sample/prebid-config.yaml Outdated Show resolved Hide resolved
long hashInt = Math.abs(greenbidsId.hashCode());
hashInt = hashInt % 0x10000;

final boolean isPrimarySampled = hashInt < exploratorySamplingRate * 0xFFFF;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not plain integer?

Copy link
Contributor Author

@EvgeniiMunin EvgeniiMunin May 13, 2024

Choose a reason for hiding this comment

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

we apply here the hexadecimal as we use it on the side of Greenbids analytics server. In this case we ensure that on the given auction we apply the hash function is applied similarly on both PBS and analytics server. And we use the same approach in Prebid.js analytics adapter

    def exploration_value(self):
        hash_value = int(greenbidsId[-4:], 16)
        max_value = 0xFFFF + 1
        return hash_value / max_value

and then compare it with the exploration_value < exploration_rate or exploration_value > (1 - exploration_rate)

final double throttledSamplingRate = samplingRate * (1.0 - this.exploratorySamplingSplit);

long hashInt = Math.abs(greenbidsId.hashCode());
hashInt = hashInt % 0x10000;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not plain integer? why is it calculated in two steps?

Copy link
Contributor Author

@EvgeniiMunin EvgeniiMunin May 13, 2024

Choose a reason for hiding this comment

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

Concerning the conversion to hexadecimal similar as in previous comment.

Concerning two steps yes I agree, I've modified Math.abs(greenbidsId.hashCode()) % 0x10000 in a one step

@EvgeniiMunin
Copy link
Contributor Author

Hi the fixes are made, @AntoxaAntoxic could you please have a look 🙏

Copy link
Collaborator

@AntoxaAntoxic AntoxaAntoxic left a comment

Choose a reason for hiding this comment

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

The next package of comments

Comment on lines 140 to 144
public Future<CommonMessage> createBidMessage(
AuctionContext auctionContext,
BidResponse bidResponse,
String greenbidsId,
String billingId) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same, it's no necessary that each method returns future

also why is the method public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have reverted changes for createBidMessage returning futures.

I made this method public because I then call it in unit test GreenbidsAnalyticsReporterTest. This is because in unit test I check the content of the payload without sending it to analytics server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Otherwise if I make the method createBidMessage private then in UT I will need to call processEvent instead and mock both creation of a payload and send to the mocked server.

The analytics server could be mocked using PowerMock but in PBS I did not find that it was used before

Comment on lines 13 to 14
@JsonProperty("version")
private final String version;
Copy link
Collaborator

Choose a reason for hiding this comment

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

it's not necessary to annotate if the field name is a single lowercased word otherwise it's better to check whether our jackson mapper serialize in a way you require

Comment on lines 86 to 87
final ObjectMapper mapper = Objects.requireNonNull(jacksonMapper).mapper()
.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
Copy link
Collaborator

@AntoxaAntoxic AntoxaAntoxic Jun 6, 2024

Choose a reason for hiding this comment

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

I don't see the problem in adding @JsonProperty annotation, but if you really want your own mapper then please create it separately without changing the existing one, in that way you won't be dependent on the changes that might be done on the higher level that could bring backward compatibility problems in future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I think I can just create a custom JacksonMapper specifically for our module

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have defined custom mapper GreenbidsJacksonMapper and set naming strategy lower camel case into its ObjectMapper

cc @AlexisBRENON

AntoxaAntoxic
AntoxaAntoxic previously approved these changes Jun 11, 2024
Copy link
Collaborator

@SerhiiNahornyi SerhiiNahornyi left a comment

Choose a reason for hiding this comment

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

It has been a long journey, but now it is over. ✅
LGTM 👍

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.

None yet

5 participants