From 43b98940eccdb7780348ed52e82a9478460be487 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Fri, 16 Jun 2023 09:45:03 -0400 Subject: [PATCH] Add public VendorList interface method to get GVL specification version (#37) --- api/vendorlist.go | 3 ++ vendorlist/eager-parsing.go | 11 +++++-- vendorlist/lazy-parsing.go | 7 +++++ vendorlist/shared_test.go | 1 + vendorlist2/eager-parsing.go | 23 +++++++++------ vendorlist2/eager-parsing_test.go | 48 ++++++++++++++++++------------- vendorlist2/lazy-parsing.go | 11 +++++-- vendorlist2/lazy-parsing_test.go | 48 ++++++++++++++++++------------- vendorlist2/shared_test.go | 2 +- 9 files changed, 101 insertions(+), 53 deletions(-) diff --git a/api/vendorlist.go b/api/vendorlist.go index eaf2785..f90d140 100644 --- a/api/vendorlist.go +++ b/api/vendorlist.go @@ -5,6 +5,9 @@ import "github.com/prebid/go-gdpr/consentconstants" // VendorList is an interface used to fetch information about an IAB Global Vendor list. // For the latest version, see: https://vendorlist.consensu.org/vendorlist.json type VendorList interface { + // SpecVersion returns the version of the global vendor list specification the list adheres to + SpecVersion() uint16 + // Version returns the version of the vendor list which this is. // // If the input was malformed, this will return 0. diff --git a/vendorlist/eager-parsing.go b/vendorlist/eager-parsing.go index b84bf95..8a8dd9c 100644 --- a/vendorlist/eager-parsing.go +++ b/vendorlist/eager-parsing.go @@ -30,6 +30,7 @@ func ParseEagerly(data []byte) (api.VendorList, error) { } parsedList := parsedVendorList{ + specVersion: contract.GVLSpecificationVersion, version: contract.Version, vendors: make(map[uint16]parsedVendor, len(contract.Vendors)), } @@ -61,8 +62,13 @@ func mapify(input []uint8) map[consentconstants.Purpose]struct{} { } type parsedVendorList struct { - version uint16 - vendors map[uint16]parsedVendor + specVersion uint16 + version uint16 + vendors map[uint16]parsedVendor +} + +func (l parsedVendorList) SpecVersion() uint16 { + return l.specVersion } func (l parsedVendorList) Version() uint16 { @@ -117,6 +123,7 @@ func (l parsedVendor) SpecialFeature(featureID consentconstants.SpecialFeature) } type vendorListContract struct { + GVLSpecificationVersion uint16 `json:"gvlSpecificationVersion"` Version uint16 `json:"vendorListVersion"` Vendors []vendorListVendorContract `json:"vendors"` } diff --git a/vendorlist/lazy-parsing.go b/vendorlist/lazy-parsing.go index bdf061c..092b560 100644 --- a/vendorlist/lazy-parsing.go +++ b/vendorlist/lazy-parsing.go @@ -22,6 +22,13 @@ func ParseLazily(data []byte) api.VendorList { type lazyVendorList []byte +func (l lazyVendorList) SpecVersion() uint16 { + if val, ok := lazyParseInt(l, "gvlSpecificationVersion"); ok { + return uint16(val) + } + return 0 +} + func (l lazyVendorList) Version() uint16 { if val, ok := lazyParseInt(l, "vendorListVersion"); ok { return uint16(val) diff --git a/vendorlist/shared_test.go b/vendorlist/shared_test.go index 55795c7..056b2a0 100644 --- a/vendorlist/shared_test.go +++ b/vendorlist/shared_test.go @@ -15,6 +15,7 @@ func vendorListTester(parser func(data []byte) api.VendorList) func(*testing.T) return func(t *testing.T) { list := parser([]byte(testData)) assertIntsEqual(t, 5, int(list.Version())) + assertIntsEqual(t, 0, int(list.SpecVersion())) assertNil(t, list.Vendor(2), true) assertNil(t, list.Vendor(32), false) } diff --git a/vendorlist2/eager-parsing.go b/vendorlist2/eager-parsing.go index 718ea98..5f797ae 100644 --- a/vendorlist2/eager-parsing.go +++ b/vendorlist2/eager-parsing.go @@ -12,8 +12,8 @@ import ( // The returned object can be shared safely between goroutines. // // This is ideal if: -// 1. You plan to call functions on the returned VendorList many times before discarding it. -// 2. You need strong input validation and good error messages. +// 1. You plan to call functions on the returned VendorList many times before discarding it. +// 2. You need strong input validation and good error messages. // // Otherwise, you may get better performance with ParseLazily. func ParseEagerly(data []byte) (api.VendorList, error) { @@ -27,8 +27,9 @@ func ParseEagerly(data []byte) (api.VendorList, error) { } parsedList := parsedVendorList{ - version: contract.Version, - vendors: make(map[uint16]parsedVendor, len(contract.Vendors)), + specVersion: contract.GVLSpecificationVersion, + version: contract.Version, + vendors: make(map[uint16]parsedVendor, len(contract.Vendors)), } for _, v := range contract.Vendors { @@ -69,8 +70,13 @@ func mapifySpecialFeature(input []uint8) map[consentconstants.SpecialFeature]str } type parsedVendorList struct { - version uint16 - vendors map[uint16]parsedVendor + specVersion uint16 + version uint16 + vendors map[uint16]parsedVendor +} + +func (l parsedVendorList) SpecVersion() uint16 { + return l.specVersion } func (l parsedVendorList) Version() uint16 { @@ -138,8 +144,9 @@ func (l parsedVendor) SpecialFeature(featureID consentconstants.SpecialFeature) } type vendorListContract struct { - Version uint16 `json:"vendorListVersion"` - Vendors map[string]vendorListVendorContract `json:"vendors"` + GVLSpecificationVersion uint16 `json:"gvlSpecificationVersion"` + Version uint16 `json:"vendorListVersion"` + Vendors map[string]vendorListVendorContract `json:"vendors"` } type vendorListVendorContract struct { diff --git a/vendorlist2/eager-parsing_test.go b/vendorlist2/eager-parsing_test.go index bd530c0..6103417 100644 --- a/vendorlist2/eager-parsing_test.go +++ b/vendorlist2/eager-parsing_test.go @@ -7,20 +7,23 @@ import ( ) func TestParseEagerlyVendorList(t *testing.T) { - tests := []struct{ - name string - vendorList string - vendorListVersion uint16 + tests := []struct { + name string + vendorList string + vendorListSpecVersion uint16 + vendorListVersion uint16 }{ { - name: "vendor list spec 2", - vendorList: testDataSpecVersion2, - vendorListVersion: 28, + name: "vendor_list_spec_2", + vendorList: testDataSpecVersion2, + vendorListSpecVersion: 2, + vendorListVersion: 28, }, { - name: "vendor list spec 3", - vendorList: testDataSpecVersion3, - vendorListVersion: 1, + name: "vendor_list_spec_3", + vendorList: testDataSpecVersion3, + vendorListSpecVersion: 3, + vendorListVersion: 1, }, } @@ -28,6 +31,7 @@ func TestParseEagerlyVendorList(t *testing.T) { t.Run(tt.name, func(t *testing.T) { parsedGVL, err := ParseEagerly([]byte(tt.vendorList)) assert.NoError(t, err) + assert.Equal(t, tt.vendorListSpecVersion, parsedGVL.SpecVersion()) assert.Equal(t, tt.vendorListVersion, parsedGVL.Version()) assert.NotNil(t, parsedGVL.Vendor(8)) assert.NotNil(t, parsedGVL.Vendor(80)) @@ -37,20 +41,23 @@ func TestParseEagerlyVendorList(t *testing.T) { } func TestParseEagerlyEmptyVendorList(t *testing.T) { - tests := []struct{ - name string - vendorList string - vendorListVersion uint16 + tests := []struct { + name string + vendorList string + vendorListSpecVersion uint16 + vendorListVersion uint16 }{ { - name: "vendor list spec 2", - vendorList: testDataSpecVersion2Empty, - vendorListVersion: 28, + name: "vendor_list_spec_2", + vendorList: testDataSpecVersion2Empty, + vendorListSpecVersion: 2, + vendorListVersion: 28, }, { - name: "vendor list spec 3", - vendorList: testDataSpecVersion3Empty, - vendorListVersion: 1, + name: "vendor_list_spec_3", + vendorList: testDataSpecVersion3Empty, + vendorListSpecVersion: 3, + vendorListVersion: 1, }, } @@ -58,6 +65,7 @@ func TestParseEagerlyEmptyVendorList(t *testing.T) { t.Run(tt.name, func(t *testing.T) { parsedGVL, err := ParseEagerly([]byte(tt.vendorList)) assert.NoError(t, err) + assert.Equal(t, tt.vendorListSpecVersion, parsedGVL.SpecVersion()) assert.Equal(t, tt.vendorListVersion, parsedGVL.Version()) assert.Nil(t, parsedGVL.Vendor(8)) assert.Nil(t, parsedGVL.Vendor(80)) diff --git a/vendorlist2/lazy-parsing.go b/vendorlist2/lazy-parsing.go index ec5dd22..7b1f69e 100644 --- a/vendorlist2/lazy-parsing.go +++ b/vendorlist2/lazy-parsing.go @@ -12,8 +12,8 @@ import ( // The returned object can be shared safely between goroutines. // // This is ideal if: -// 1. You only need to look up a few vendors or purpose IDs -// 2. You don't need good errors on malformed input +// 1. You only need to look up a few vendors or purpose IDs +// 2. You don't need good errors on malformed input // // Otherwise, you may get better performance with ParseEagerly. func ParseLazily(data []byte) api.VendorList { @@ -22,6 +22,13 @@ func ParseLazily(data []byte) api.VendorList { type lazyVendorList []byte +func (l lazyVendorList) SpecVersion() uint16 { + if val, ok := lazyParseInt(l, "gvlSpecificationVersion"); ok { + return uint16(val) + } + return 0 +} + func (l lazyVendorList) Version() uint16 { if val, ok := lazyParseInt(l, "vendorListVersion"); ok { return uint16(val) diff --git a/vendorlist2/lazy-parsing_test.go b/vendorlist2/lazy-parsing_test.go index dc9213c..3884a19 100644 --- a/vendorlist2/lazy-parsing_test.go +++ b/vendorlist2/lazy-parsing_test.go @@ -7,26 +7,30 @@ import ( ) func TestParseLazilyVendorList(t *testing.T) { - tests := []struct{ - name string - vendorList string - vendorListVersion uint16 + tests := []struct { + name string + vendorList string + vendorListSpecVersion uint16 + vendorListVersion uint16 }{ { - name: "vendor list spec 2", - vendorList: testDataSpecVersion2, - vendorListVersion: 28, + name: "vendor_list_spec_2", + vendorList: testDataSpecVersion2, + vendorListSpecVersion: 2, + vendorListVersion: 28, }, { - name: "vendor list spec 3", - vendorList: testDataSpecVersion3, - vendorListVersion: 1, + name: "vendor_list_spec_3", + vendorList: testDataSpecVersion3, + vendorListSpecVersion: 3, + vendorListVersion: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parsedGVL := ParseLazily([]byte(tt.vendorList)) + assert.Equal(t, tt.vendorListSpecVersion, parsedGVL.SpecVersion()) assert.Equal(t, tt.vendorListVersion, parsedGVL.Version()) assert.NotNil(t, parsedGVL.Vendor(8)) assert.NotNil(t, parsedGVL.Vendor(80)) @@ -36,26 +40,30 @@ func TestParseLazilyVendorList(t *testing.T) { } func TestParseLazilyEmptyVendorList(t *testing.T) { - tests := []struct{ - name string - vendorList string - vendorListVersion uint16 + tests := []struct { + name string + vendorList string + vendorListSpecVersion uint16 + vendorListVersion uint16 }{ { - name: "vendor list spec 2", - vendorList: testDataSpecVersion2Empty, - vendorListVersion: 28, + name: "vendor_list_spec_2", + vendorList: testDataSpecVersion2Empty, + vendorListSpecVersion: 2, + vendorListVersion: 28, }, { - name: "vendor list spec 3", - vendorList: testDataSpecVersion3Empty, - vendorListVersion: 1, + name: "vendor_list_spec_3", + vendorList: testDataSpecVersion3Empty, + vendorListSpecVersion: 3, + vendorListVersion: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { parsedGVL := ParseLazily([]byte(tt.vendorList)) + assert.Equal(t, tt.vendorListSpecVersion, parsedGVL.SpecVersion()) assert.Equal(t, tt.vendorListVersion, parsedGVL.Version()) assert.Nil(t, parsedGVL.Vendor(8)) assert.Nil(t, parsedGVL.Vendor(80)) diff --git a/vendorlist2/shared_test.go b/vendorlist2/shared_test.go index b84fa85..f97d3e2 100644 --- a/vendorlist2/shared_test.go +++ b/vendorlist2/shared_test.go @@ -60,7 +60,7 @@ func AssertVendorListCorrectness(t *testing.T, gvl api.VendorList) { assertBoolsEqual(t, false, v.SpecialPurpose(1)) assertBoolsEqual(t, false, v.SpecialPurpose(2)) assertBoolsEqual(t, false, v.SpecialPurpose(3)) // Does not exist yet - + assertBoolsEqual(t, false, v.SpecialFeature(1)) assertBoolsEqual(t, false, v.SpecialFeature(2)) assertBoolsEqual(t, false, v.SpecialFeature(3)) // Does not exist yet