forked from prebid/prebid-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First Party Data: Updates OpenRTB Field Merging (prebid#2825)
Co-authored-by: VeronikaSolovei9 <kalypsonika@gmail.com>
- Loading branch information
1 parent
4c3b2c1
commit 833101c
Showing
39 changed files
with
1,638 additions
and
1,318 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package firstpartydata | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/prebid/prebid-server/util/sliceutil" | ||
jsonpatch "gopkg.in/evanphx/json-patch.v4" | ||
) | ||
|
||
var ( | ||
ErrBadRequest = fmt.Errorf("invalid request ext") | ||
ErrBadFPD = fmt.Errorf("invalid first party data ext") | ||
) | ||
|
||
// extMerger tracks a JSON `ext` field within an OpenRTB request. The value of the | ||
// `ext` field is expected to be modified when calling unmarshal on the same object | ||
// and will later be updated when invoking Merge. | ||
type extMerger struct { | ||
ext *json.RawMessage // Pointer to the JSON `ext` field. | ||
snapshot json.RawMessage // Copy of the original state of the JSON `ext` field. | ||
} | ||
|
||
// Track saves a copy of the JSON `ext` field and stores a reference to the extension | ||
// object for comparison later in the Merge call. | ||
func (e *extMerger) Track(ext *json.RawMessage) { | ||
e.ext = ext | ||
e.snapshot = sliceutil.Clone(*ext) | ||
} | ||
|
||
// Merge applies a JSON merge of the stored extension snapshot on top of the current | ||
// JSON of the tracked extension object. | ||
func (e extMerger) Merge() error { | ||
if e.ext == nil { | ||
return nil | ||
} | ||
|
||
if len(e.snapshot) == 0 { | ||
return nil | ||
} | ||
|
||
if len(*e.ext) == 0 { | ||
*e.ext = e.snapshot | ||
return nil | ||
} | ||
|
||
merged, err := jsonpatch.MergePatch(e.snapshot, *e.ext) | ||
if err != nil { | ||
if errors.Is(err, jsonpatch.ErrBadJSONDoc) { | ||
return ErrBadRequest | ||
} else if errors.Is(err, jsonpatch.ErrBadJSONPatch) { | ||
return ErrBadFPD | ||
} | ||
return err | ||
} | ||
|
||
*e.ext = merged | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package firstpartydata | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/util/sliceutil" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestExtMerger(t *testing.T) { | ||
t.Run("nil", func(t *testing.T) { | ||
merger := extMerger{ext: nil, snapshot: json.RawMessage(`{"a":1}`)} | ||
assert.NoError(t, merger.Merge()) | ||
assert.Nil(t, merger.ext) | ||
}) | ||
|
||
testCases := []struct { | ||
name string | ||
givenOriginal json.RawMessage | ||
givenFPD json.RawMessage | ||
expectedExt json.RawMessage | ||
expectedErr string | ||
}{ | ||
{ | ||
name: "both-populated", | ||
givenOriginal: json.RawMessage(`{"a":1,"b":2}`), | ||
givenFPD: json.RawMessage(`{"b":200,"c":3}`), | ||
expectedExt: json.RawMessage(`{"a":1,"b":200,"c":3}`), | ||
}, | ||
{ | ||
name: "both-nil", | ||
givenFPD: nil, | ||
givenOriginal: nil, | ||
expectedExt: nil, | ||
}, | ||
{ | ||
name: "both-empty", | ||
givenOriginal: json.RawMessage(`{}`), | ||
givenFPD: json.RawMessage(`{}`), | ||
expectedExt: json.RawMessage(`{}`), | ||
}, | ||
{ | ||
name: "ext-nil", | ||
givenOriginal: json.RawMessage(`{"b":2}`), | ||
givenFPD: nil, | ||
expectedExt: json.RawMessage(`{"b":2}`), | ||
}, | ||
{ | ||
name: "ext-empty", | ||
givenOriginal: json.RawMessage(`{"b":2}`), | ||
givenFPD: json.RawMessage(`{}`), | ||
expectedExt: json.RawMessage(`{"b":2}`), | ||
}, | ||
{ | ||
name: "ext-malformed", | ||
givenOriginal: json.RawMessage(`{"b":2}`), | ||
givenFPD: json.RawMessage(`malformed`), | ||
expectedErr: "invalid first party data ext", | ||
}, | ||
{ | ||
name: "snapshot-nil", | ||
givenOriginal: nil, | ||
givenFPD: json.RawMessage(`{"a":1}`), | ||
expectedExt: json.RawMessage(`{"a":1}`), | ||
}, | ||
{ | ||
name: "snapshot-empty", | ||
givenOriginal: json.RawMessage(`{}`), | ||
givenFPD: json.RawMessage(`{"a":1}`), | ||
expectedExt: json.RawMessage(`{"a":1}`), | ||
}, | ||
{ | ||
name: "snapshot-malformed", | ||
givenOriginal: json.RawMessage(`malformed`), | ||
givenFPD: json.RawMessage(`{"a":1}`), | ||
expectedErr: "invalid request ext", | ||
}, | ||
} | ||
|
||
for _, test := range testCases { | ||
t.Run(test.name, func(t *testing.T) { | ||
// Initialize A Ext Raw Message For Testing | ||
simulatedExt := json.RawMessage(sliceutil.Clone(test.givenOriginal)) | ||
|
||
// Begin Tracking | ||
var merger extMerger | ||
merger.Track(&simulatedExt) | ||
|
||
// Unmarshal | ||
simulatedExt.UnmarshalJSON(test.givenFPD) | ||
|
||
// Merge | ||
actualErr := merger.Merge() | ||
|
||
if test.expectedErr == "" { | ||
assert.NoError(t, actualErr, "error") | ||
|
||
if test.expectedExt == nil { | ||
assert.Nil(t, simulatedExt, "json") | ||
} else { | ||
assert.JSONEq(t, string(test.expectedExt), string(simulatedExt), "json") | ||
} | ||
} else { | ||
assert.EqualError(t, actualErr, test.expectedErr, "error") | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.