-
Notifications
You must be signed in to change notification settings - Fork 748
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
GPC: Set extension based on header #3895
Changes from all commits
8971a36
58aab7c
b7692d7
a2b7cf1
c7fbfe1
f713ff1
5fba3db
f8e7977
b5acce3
bff49e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5618,6 +5618,175 @@ func TestValidateOrFillCookieDeprecation(t *testing.T) { | |
} | ||
} | ||
|
||
func TestSetGPCImplicitly(t *testing.T) { | ||
testCases := []struct { | ||
description string | ||
header string | ||
regs *openrtb2.Regs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: move |
||
expectError bool | ||
expectedRegs *openrtb2.Regs | ||
}{ | ||
{ | ||
description: "regs_ext_gpc_not_set_and_header_is_1", | ||
header: "1", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_not_set_gpc_should_not_be_modified", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest covering the following test cases: |
||
header: "", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_set_to_2_gpc_should_not_be_modified", | ||
header: "2", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "sec_gpc_header_set_to_1_and_regs_ext_contains_other_data", | ||
header: "1", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{"some_other_field":"some_value"}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"some_other_field":"some_value","gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_ext_gpc_not_set_and_header_not_set", | ||
header: "", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_ext_gpc_not_set_and_header_not_1", | ||
header: "0", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_ext_gpc_is_1_and_header_is_1", | ||
header: "1", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_ext_gpc_is_1_and_header_not_1", | ||
header: "0", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_ext_other_data_and_header_is_1", | ||
header: "1", | ||
regs: &openrtb2.Regs{ | ||
Ext: []byte(`{"other":"value"}`), | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"other":"value","gpc":"1"}`), | ||
}, | ||
}, | ||
{ | ||
description: "regs_nil_and_header_is_1", | ||
header: "1", | ||
regs: nil, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: []byte(`{"gpc":"1"}`), | ||
}, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to add these two test cases to ensure that we don't create a regs or regs.ext object if we're not writing to the
|
||
{ | ||
description: "regs_nil_and_header_not_set", | ||
header: "", | ||
regs: nil, | ||
expectError: false, | ||
expectedRegs: nil, | ||
}, | ||
{ | ||
description: "regs_ext_is_nil_and_header_not_set", | ||
header: "", | ||
regs: &openrtb2.Regs{ | ||
Ext: nil, | ||
}, | ||
expectError: false, | ||
expectedRegs: &openrtb2.Regs{ | ||
Ext: nil, | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
httpReq := &http.Request{ | ||
Header: http.Header{ | ||
http.CanonicalHeaderKey("Sec-GPC"): []string{test.header}, | ||
}, | ||
} | ||
|
||
r := &openrtb_ext.RequestWrapper{ | ||
BidRequest: &openrtb2.BidRequest{ | ||
Regs: test.regs, | ||
}, | ||
} | ||
|
||
err := setGPCImplicitly(httpReq, r) | ||
|
||
if test.expectError { | ||
assert.Error(t, err) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
assert.NoError(t, r.RebuildRequest()) | ||
if test.expectedRegs == nil { | ||
assert.Nil(t, r.BidRequest.Regs) | ||
} else if test.expectedRegs.Ext == nil { | ||
assert.Nil(t, r.BidRequest.Regs.Ext) | ||
} else { | ||
assert.JSONEq(t, string(test.expectedRegs.Ext), string(r.BidRequest.Regs.Ext)) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestValidateRequestCookieDeprecation(t *testing.T) { | ||
testCases := | ||
[]struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code looks good. Please update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you missed this during your last iteration but please add/update the wrapper tests to cover changes. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2174,6 +2174,30 @@ func TestRebuildRegExt(t *testing.T) { | |
regExt: RegExt{usPrivacy: "", usPrivacyDirty: true}, | ||
expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, | ||
}, | ||
{ | ||
name: "req_regs_gpc_populated_-_not_dirty_-_no_change", | ||
request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
regExt: RegExt{}, | ||
expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
}, | ||
{ | ||
name: "req_regs_gpc_populated_-_dirty_and_different-_change", | ||
request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
regExt: RegExt{gpc: &strB, gpcDirty: true}, | ||
expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"b"}`)}}, | ||
}, | ||
{ | ||
name: "req_regs_gpc_populated_-_dirty_and_same_-_no_change", | ||
request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
regExt: RegExt{gpc: &strA, gpcDirty: true}, | ||
expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
}, | ||
{ | ||
name: "req_regs_gpc_populated_-_dirty_and_nil_-_cleared", | ||
request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gpc":"a"}`)}}, | ||
regExt: RegExt{gpc: nil, gpcDirty: true}, | ||
expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
|
@@ -2194,6 +2218,7 @@ func TestRegExtUnmarshal(t *testing.T) { | |
extJson json.RawMessage | ||
expectDSA *ExtRegsDSA | ||
expectGDPR *int8 | ||
expectGPC *string | ||
expectUSPrivacy string | ||
expectError bool | ||
}{ | ||
|
@@ -2253,6 +2278,21 @@ func TestRegExtUnmarshal(t *testing.T) { | |
expectGDPR: ptrutil.ToPtr[int8](0), | ||
expectError: true, | ||
}, | ||
// GPC | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests look good as they adequately cover your changes to the
|
||
{ | ||
name: "valid_gpc_json", | ||
regExt: &RegExt{}, | ||
extJson: json.RawMessage(`{"gpc":"some_value"}`), | ||
expectGPC: ptrutil.ToPtr("some_value"), | ||
expectError: false, | ||
}, | ||
{ | ||
name: "malformed_gpc_json", | ||
regExt: &RegExt{}, | ||
extJson: json.RawMessage(`{"gpc":nill}`), | ||
expectGPC: nil, | ||
expectError: true, | ||
}, | ||
// us_privacy | ||
{ | ||
name: "valid_usprivacy_json", | ||
|
@@ -2348,3 +2388,18 @@ func TestRegExtGetGDPRSetGDPR(t *testing.T) { | |
assert.Equal(t, regExtGDPR, gdpr) | ||
assert.NotSame(t, regExtGDPR, gdpr) | ||
} | ||
|
||
func TestRegExtGetGPCSetGPC(t *testing.T) { | ||
regExt := &RegExt{} | ||
regExtGPC := regExt.GetGPC() | ||
assert.Nil(t, regExtGPC) | ||
assert.False(t, regExt.Dirty()) | ||
|
||
gpc := ptrutil.ToPtr("Gpc") | ||
regExt.SetGPC(gpc) | ||
assert.True(t, regExt.Dirty()) | ||
|
||
regExtGPC = regExt.GetGPC() | ||
assert.Equal(t, regExtGPC, gpc) | ||
assert.NotSame(t, regExtGPC, gpc) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest adding the following early return just before the set:
This way we won't perform an unnecessary write which sets the dirty flag and causes extra work when rebuilding the request downstream.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @przemkaczmarek, here is a super nitpick to this function.
In the issue description the logic is straightforward:
The code here does the right thing, however it's a little confusing and difficult to read. I had to read it couple of times to understand it.
Here is a suggestion that I think traces better to the requirements and might be better for readability:
The unit test passes as is.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my version of the code, I first check the value of secGPC, and if it doesn't meet the condition, the function ends immediately with return nil. In your version of the code, this happens later, which leads to unnecessary retrieval of regExt, even when it's not needed. I avoid unnecessary operations. I avoid performing costly operations (such as r.GetRegExt()) if the secGPC header does not have the value '1'. In the first version, you retrieve the regExt extension regardless of the secGPC value, which is less optimal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I understand this. I am only concerned about the readability.
Also this is a minor performance optimization (but still an optimization!) so it's up to you to change it.
I approve this PR, and if you decide to change it - I'll re-approve it right away too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to keep the code as is but thanks for the comment