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

TheMediaGrid: Add params.keywords processing #1913

Merged
merged 57 commits into from
Aug 5, 2021
Merged

Conversation

TheMediaGrid
Copy link
Contributor

Update s2s TheMediaGrid adapter to read imp.ext.bidder.keywords and copy it in ext.keywords

@SyntaxNode SyntaxNode assigned bsardo and unassigned hhhjort Jul 19, 2021
@SyntaxNode SyntaxNode requested review from bsardo and removed request for hhhjort July 19, 2021 17:35
Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

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

Left some comments

var reqExt ReqExt
var reqExtKeywords map[string]interface{}

if err := json.Unmarshal(request.Ext, &reqExt); err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

In order to have less nested statements can we return if error instead?

    if err := json.Unmarshal(request.Ext, &reqExt); err != nil {
        return err
    }

// ExtImpGrid defines the contract for bidrequest.imp[i].ext.grid
type ExtImpGrid struct {
Uid int `json:"uid"`
Uid int `json:"uid"`
Keywords json.RawMessage `json:"keywords,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we please add the keywords parameter in static/bidder-params/grid.json with its proper constraints if applicable? (required or not, minlength or none)

}
}

func reformatExtKeywords(extKeywords map[string]interface{}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we refactor the reformatExtKeywords(extKeywords map[string]interface{}) function or subdivide it into subfunctions to make it easier to read?

for _, item := range pubArr {
switch item.(type) {
// default:
// formatedPubArr = append(formatedPubArr, item)
Copy link
Contributor

Choose a reason for hiding this comment

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

if these couple of lines are of no use, can we remove?


func reformatExtKeywords(extKeywords map[string]interface{}) {
for name, pubData := range extKeywords {
switch pubData.(type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that the type switch statements in lines 160 and 167 only seem to have one case statement, can we use a type assertion instead?

if v, ok = x.(T); ok {
    // ...
}

Or even the reflect library:

x := 42

xtype := reflect.TypeOf(x).Kind()

if xtype == reflect.Int {
    println("x is int")
}

Copy link
Contributor

@SyntaxNode SyntaxNode left a comment

Choose a reason for hiding this comment

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

I completely agree with the BidMeta changes to PBS-Core to solve the overwriting problem.

type TypedBid struct {
Bid *openrtb2.Bid
BidType openrtb_ext.BidType
BidVideo *openrtb_ext.ExtBidPrebidVideo
DealPriority int
BidMeta *openrtb_ext.ExtBidPrebidMeta
Copy link
Contributor

Choose a reason for hiding this comment

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

Please include BidMeta alphabetically in the comments and TypedBid object.

@@ -71,6 +72,7 @@ type pbsOrtbBid struct {
dealPriority int
dealTierSatisfied bool
generatedBidID string
bidMeta *openrtb_ext.ExtBidPrebidMeta
Copy link
Contributor

Choose a reason for hiding this comment

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

Please include bidMeta alphabetically in the comments and pbsOrtbBid object.

reqJSON, err := json.Marshal(requestCopy)
err := setImpExtKeywords(validImps[0], request)
if err != nil {
errors = append(errors, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the error thrown in setImpExtKeywords not worth returning nil? Among the potential errors that setImpExtKeywords may throw are Unmarshal and Marshal errors. In those cases do we still not want to do:

		return nil, errors

?

var extKWSite map[string]interface{}
var extKWUser map[string]interface{}

json.Unmarshal(keywords, &extKeywords)
Copy link
Contributor

Choose a reason for hiding this comment

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

We realize an error scenario here is unlikely but we believe it's better to try to check for it and handle it

return err
}
if reqExt.Keywords != nil {
json.Unmarshal(reqExt.Keywords, &reqExtKeywords)
Copy link
Contributor

Choose a reason for hiding this comment

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

We realize an error scenario here is unlikely but we believe it's better to try to check for it and handle it

}
},
"required": []
"required": ["uid"]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you have any statistics on how many requests come in currently without a "uid" parameter? There was a design choice that any request that does not pass validation on any one of the bidder JSON validation schemas fails entirely, not just for the one bidder. This was done as when a publisher sets up a new configuration with multiple bidders, a bidder parameter failure on one bidder may be masked by all the successful bids from other bidders. We would rather the requests were correct from the start.

This means any current bidder configs that do not include uid will fail to fetch ads from any bidder. This will break publisher configs across all bidders, not just yours. The preferred way to add a new required field is to initially list it as optional in the schema, and have the adapter return an error if it is missing. An adapter returning an error outside of JSON schema validation will not invalidate the whole request. Then once we all have some confidence that all the publishers have updated their configs, put in a followup PR to change the schema to required.

@TheMediaGrid
Copy link
Contributor Author

@guscarreon @SyntaxNode @hhhjort any chance you could take a look at the most recent changes that we've made as per Scott's comments? Any insight into why the auto tests are failing would be very useful as well.

@SyntaxNode
Copy link
Contributor

@guscarreon @SyntaxNode @hhhjort any chance you could take a look at the most recent changes that we've made as per Scott's comments? Any insight into why the auto tests are failing would be very useful as well.

I'm looking into it. I didn't see this locally either. I suspect it's a case where the segments array order is indeterminate, but will confirm.

@SyntaxNode
Copy link
Contributor

@guscarreon @SyntaxNode @hhhjort any chance you could take a look at the most recent changes that we've made as per Scott's comments? Any insight into why the auto tests are failing would be very useful as well.

I'm looking into it. I didn't see this locally either. I suspect it's a case where the segments array order is indeterminate, but will confirm.

Found it.

The issue is caused by the indeterminate ordering of the segments when constructed from an array of strings, referred to as the "compatible alternate segment format" in the code.

The ordering relies on the iteration order of keys within a map, which is purposefully inconsistent in Go. I don't believe the ordering matters to your bidding server, but it does matter to the json tests since arrays are always tested as order dependent. I'm not sure how easily or safely we can modify the test layer, but it easy to sort the keys to ensure deterministic order.

Change:

// compose compatible alternate segment format
for potentialSegmentName, potentialSegmentValues := range publisherItem {
	// values must be an array
	if valuesSlice, ok := potentialSegmentValues.([]interface{}); ok {
		for _, value := range valuesSlice {
			if valueAsString, ok := value.(string); ok {
				segments = append(segments, KeywordSegment{Name: potentialSegmentName, Value: valueAsString})
			}
		}
	}
}

To:

// ensure consistent ordering for publisher item map
publisherItemKeys := make([]string, 0, len(publisherItem))
for v := range publisherItem {
	publisherItemKeys = append(publisherItemKeys, v)
}
sort.Strings(publisherItemKeys)

// compose compatible alternate segment format
for _, potentialSegmentName := range publisherItemKeys {
	potentialSegmentValues := publisherItem[potentialSegmentName]
	// values must be an array
	if valuesSlice, ok := potentialSegmentValues.([]interface{}); ok {
		for _, value := range valuesSlice {
			if valueAsString, ok := value.(string); ok {
				segments = append(segments, KeywordSegment{Name: potentialSegmentName, Value: valueAsString})
			}
		}
	}
}

This will then consistently fail until the mixed test is updated to the following order:

"segments": [
    { "name": "segName1", "value": "segVal1" },
    { "name": "segName2", "value": "segVal2" },
    { "name": "bottom", "value": "bottomKey1" },
    { "name": "bottom", "value": "bottomKey2" },
    { "name": "segments", "value": "stringKey" }
]

After which the tests will consistently passes. I don't believe this is a new issue, but was missed earlier just by chance.

@TheMediaGrid
Copy link
Contributor Author

@SyntaxNode @guscarreon @hhhjort @bsardo can anybody please review this PR? it's been a while

@@ -19,3 +19,12 @@ func ReadEmbeddedSlice(m map[string]interface{}, k string) ([]interface{}, bool)

return nil, false
}

// ReadEmbeddedString reads element k from the map m as a string
func ReadEmbeddedString(m map[string]interface{}, k string) (string, bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please add test coverage for this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bsardo we added the test as requested

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 looks good

@@ -0,0 +1,308 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

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

My personal preference here would be to see some of the invalid cases tested in their own small test files as this is pretty hard to digest. At the same time, I recognize this test adds value as written because it exercises just about all of the keyword logic which raises confidence that it works correctly together. I can be swayed either way. @guscarreon @SyntaxNode thoughts?

Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

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

@TheMediaGrid : In order to double-check the expected behavior or your keywords algorithm could you please write 3 more tests where there are keywords:

  • In the root.Ext only
  • In the root.site only
  • In the root.user only

I think your code looks pretty solid and with these three extra tests I'd be good to approve.

Copy link
Contributor

@guscarreon guscarreon left a comment

Choose a reason for hiding this comment

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

The code looks pretty solid. It'd be nice to add 3 more tests in a follow-up PR where there are keywords:

  • In the root.Ext only
  • In the root.site only
  • In the root.user only

@bsardo bsardo merged commit 0dbf605 into prebid:master Aug 5, 2021
jizeyopera pushed a commit to operaads/prebid-server that referenced this pull request Oct 13, 2021
shunj-nb pushed a commit to ParticleMedia/prebid-server that referenced this pull request Nov 8, 2022
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