diff --git a/constants/constants.go b/constants/constants.go index 0235462..9f4a393 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -12,6 +12,9 @@ const ( SectionUSPCO SectionID = 10 SectionUSPUT SectionID = 11 SectionUSPCT SectionID = 12 + SectionUSPTX SectionID = 16 + SectionUSPOR SectionID = 15 + SectionUSPMT SectionID = 14 ) var SectionNamesByID = map[int]string{ @@ -24,4 +27,6 @@ var SectionNamesByID = map[int]string{ 10: "uspco", 11: "usput", 12: "uspct", + 16: "usptx", + 15: "uspor", } diff --git a/sections/uspmt/uspmt.go b/sections/uspmt/uspmt.go new file mode 100644 index 0000000..7020997 --- /dev/null +++ b/sections/uspmt/uspmt.go @@ -0,0 +1,167 @@ +package uspmt + +import ( + "github.com/prebid/go-gpp/constants" + "github.com/prebid/go-gpp/sections" + "github.com/prebid/go-gpp/util" +) + +type USPMTCoreSegment struct { + Version byte + SharingNotice byte + SaleOptOutNotice byte + TargetedAdvertisingOptOutNotice byte + SaleOptOut byte + TargetedAdvertisingOptOut byte + SensitiveDataProcessing []byte + KnownChildSensitiveDataConsents []byte + AdditionalDataProcessingConsent byte + MspaCoveredTransaction byte + MspaOptOutOptionMode byte + MspaServiceProviderMode byte +} + +func NewUSMTCoreSegment(bs *util.BitStream) (USPMTCoreSegment, error) { + var usmt USPMTCoreSegment + var err error + + usmt.Version, err = bs.ReadByte6() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.Version", err) + } + + usmt.SharingNotice, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.SharingNotice", err) + } + + usmt.SaleOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.SaleOptOutNotice", err) + } + + usmt.TargetedAdvertisingOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.TargetedAdvertisingOptOutNotice", err) + } + + usmt.SaleOptOut, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.SaleOptOut", err) + } + + usmt.TargetedAdvertisingOptOut, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.TargetedAdvertisingOptOut", err) + } + + usmt.SensitiveDataProcessing, err = bs.ReadTwoBitField(8) + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.SensitiveDataProcessing", err) + } + + usmt.KnownChildSensitiveDataConsents, err = bs.ReadTwoBitField(3) + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.KnownChildSensitiveDataConsentsArr", err) + } + + usmt.AdditionalDataProcessingConsent, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.AdditionalDataProcessingConsent", err) + } + + usmt.MspaCoveredTransaction, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.MspaCoveredTransaction", err) + } + + usmt.MspaOptOutOptionMode, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.MspaOptOutOptionMode", err) + } + + usmt.MspaServiceProviderMode, err = bs.ReadByte2() + if err != nil { + return usmt, sections.ErrorHelper("USMTSegment.MspaServiceProviderMode", err) + } + + return usmt, nil +} + +func (segment USPMTCoreSegment) Encode(bs *util.BitStream) { + bs.WriteByte6(segment.Version) + bs.WriteByte2(segment.SharingNotice) + bs.WriteByte2(segment.SaleOptOutNotice) + bs.WriteByte2(segment.TargetedAdvertisingOptOutNotice) + bs.WriteByte2(segment.SaleOptOut) + bs.WriteByte2(segment.TargetedAdvertisingOptOut) + bs.WriteTwoBitField(segment.SensitiveDataProcessing) + bs.WriteTwoBitField(segment.KnownChildSensitiveDataConsents) + bs.WriteByte2(segment.AdditionalDataProcessingConsent) + bs.WriteByte2(segment.MspaCoveredTransaction) + bs.WriteByte2(segment.MspaOptOutOptionMode) + bs.WriteByte2(segment.MspaServiceProviderMode) +} + +type USPMT struct { + SectionID constants.SectionID + Value string + CoreSegment USPMTCoreSegment + GPCSegment sections.CommonUSGPCSegment +} + +func NewUSPMT(encoded string) (USPMT, error) { + uspmt := USPMT{} + + coreBitStream, gpcBitStream, err := sections.CreateBitStreams(encoded, true) + if err != nil { + return uspmt, err + } + + coreSegment, err := NewUSMTCoreSegment(coreBitStream) + if err != nil { + return uspmt, err + } + + gpcSegment := sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: false, + } + + if gpcBitStream != nil { + gpcSegment, err = sections.NewCommonUSGPCSegment(gpcBitStream) + if err != nil { + return uspmt, err + } + } + + uspmt = USPMT{ + SectionID: constants.SectionUSPMT, + Value: encoded, + CoreSegment: coreSegment, + GPCSegment: gpcSegment, + } + + return uspmt, nil +} + +func (uspmt USPMT) Encode(gpcIncluded bool) []byte { + bs := util.NewBitStreamForWrite() + uspmt.CoreSegment.Encode(bs) + res := bs.Base64Encode() + if !gpcIncluded { + return res + } + bs.Reset() + res = append(res, '.') + uspmt.GPCSegment.Encode(bs) + return append(res, bs.Base64Encode()...) +} + +func (uspmt USPMT) GetID() constants.SectionID { + return uspmt.SectionID +} + +func (uspmt USPMT) GetValue() string { + return uspmt.Value +} diff --git a/sections/uspmt/uspmt_test.go b/sections/uspmt/uspmt_test.go new file mode 100644 index 0000000..8b73850 --- /dev/null +++ b/sections/uspmt/uspmt_test.go @@ -0,0 +1,62 @@ +package uspmt + +import ( + "testing" + + "github.com/prebid/go-gpp/constants" + "github.com/prebid/go-gpp/sections" + "github.com/stretchr/testify/assert" +) + +type uspmtTestData struct { + description string + gppString string + expected USPMT +} + +func TestUSPCO(t *testing.T) { + testData := []uspmtTestData{ + { + description: "should populate USPMT segments correctly", + gppString: "bSFgmAGU.YA", + /* + 011011 01 00 10 00 01 0110000010011000 000000 01 10 01 01 01 1 1110 + */ + expected: USPMT{ + CoreSegment: USPMTCoreSegment{ + Version: 27, + SharingNotice: 1, + SaleOptOutNotice: 0, + TargetedAdvertisingOptOutNotice: 2, + SaleOptOut: 0, + TargetedAdvertisingOptOut: 1, + SensitiveDataProcessing: []byte{ + 1, 2, 0, 0, 2, 1, 2, 0, + }, + KnownChildSensitiveDataConsents: []byte{0, 0, 0}, + AdditionalDataProcessingConsent: 1, + MspaCoveredTransaction: 2, + MspaOptOutOptionMode: 1, + MspaServiceProviderMode: 1, + }, + GPCSegment: sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: true, + }, + SectionID: constants.SectionUSPMT, + Value: "bSFgmAGU.YA", + }, + }, + } + + for _, test := range testData { + result, err := NewUSPMT(test.gppString) + encodedString := string(test.expected.Encode(true)) + + assert.Nil(t, err) + assert.Equal(t, test.expected, result) + assert.Equal(t, constants.SectionUSPMT, result.GetID()) + assert.Equal(t, test.gppString, result.GetValue()) + assert.Equal(t, test.gppString, encodedString) + } +} diff --git a/sections/uspor/uspor.go b/sections/uspor/uspor.go new file mode 100644 index 0000000..538dcca --- /dev/null +++ b/sections/uspor/uspor.go @@ -0,0 +1,167 @@ +package uspor + +import ( + "github.com/prebid/go-gpp/constants" + "github.com/prebid/go-gpp/sections" + "github.com/prebid/go-gpp/util" +) + +type USPORCoreSegment struct { + Version byte + ProcessingNotice byte + SaleOptOutNotice byte + TargetedAdvertisingOptOutNotice byte + SaleOptOut byte + TargetedAdvertisingOptOut byte + SensitiveDataProcessing []byte + KnownChildSensitiveDataConsents []byte + AdditionalDataProcessingConsent byte + MspaCoveredTransaction byte + MspaOptOutOptionMode byte + MspaServiceProviderMode byte +} + +type USPOR struct { + SectionID constants.SectionID + Value string + CoreSegment USPORCoreSegment + GPCSegment sections.CommonUSGPCSegment +} + +func NewUPSORCoreSegment(bs *util.BitStream) (USPORCoreSegment, error) { + var usporCore USPORCoreSegment + var err error + + usporCore.Version, err = bs.ReadByte6() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.Version", err) + } + + usporCore.ProcessingNotice, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.ProcessingNotice", err) + } + + usporCore.SaleOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.SaleOptOutNotice", err) + } + + usporCore.TargetedAdvertisingOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.TargetedAdvertisingOptOutNotice", err) + } + + usporCore.SaleOptOut, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.SaleOptOut", err) + } + + usporCore.TargetedAdvertisingOptOut, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.TargetedAdvertisingOptOut", err) + } + + usporCore.SensitiveDataProcessing, err = bs.ReadTwoBitField(11) + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.SensitiveDataProcessing", err) + } + + usporCore.KnownChildSensitiveDataConsents, err = bs.ReadTwoBitField(3) + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.KnownChildSensitiveDataConsents", err) + } + + usporCore.AdditionalDataProcessingConsent, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.AdditionalDataProcessingConsent", err) + } + + usporCore.MspaCoveredTransaction, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.MspaCoveredTransaction", err) + } + + usporCore.MspaOptOutOptionMode, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.MspaOptOutOptionMode", err) + } + + usporCore.MspaServiceProviderMode, err = bs.ReadByte2() + if err != nil { + return usporCore, sections.ErrorHelper("CoreSegment.MspaServiceProviderMode", err) + } + + return usporCore, nil +} + +func (segment USPORCoreSegment) Encode(bs *util.BitStream) { + bs.WriteByte6(segment.Version) + bs.WriteByte2(segment.ProcessingNotice) + bs.WriteByte2(segment.SaleOptOutNotice) + bs.WriteByte2(segment.TargetedAdvertisingOptOutNotice) + bs.WriteByte2(segment.SaleOptOut) + bs.WriteByte2(segment.TargetedAdvertisingOptOut) + bs.WriteTwoBitField(segment.SensitiveDataProcessing) + bs.WriteTwoBitField(segment.KnownChildSensitiveDataConsents) + bs.WriteByte2(segment.AdditionalDataProcessingConsent) + bs.WriteByte2(segment.MspaCoveredTransaction) + bs.WriteByte2(segment.MspaOptOutOptionMode) + bs.WriteByte2(segment.MspaServiceProviderMode) +} + +func NewUSPOR(encoded string) (USPOR, error) { + uspor := USPOR{} + + bitStream, gpcBitStream, err := sections.CreateBitStreams(encoded, true) + if err != nil { + return uspor, err + } + + coreSegment, err := NewUPSORCoreSegment(bitStream) + if err != nil { + return uspor, err + } + + gpcSegment := sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: false, + } + + if gpcBitStream != nil { + gpcSegment, err = sections.NewCommonUSGPCSegment(gpcBitStream) + if err != nil { + return uspor, err + } + } + + uspor = USPOR{ + SectionID: constants.SectionUSPOR, + Value: encoded, + CoreSegment: coreSegment, + GPCSegment: gpcSegment, + } + + return uspor, nil +} + +func (uspor USPOR) Encode(gpcIncluded bool) []byte { + bs := util.NewBitStreamForWrite() + uspor.CoreSegment.Encode(bs) + res := bs.Base64Encode() + if !gpcIncluded { + return res + } + bs.Reset() + res = append(res, '.') + uspor.GPCSegment.Encode(bs) + return append(res, bs.Base64Encode()...) +} + +func (uspor USPOR) GetID() constants.SectionID { + return uspor.SectionID +} + +func (uspor USPOR) GetValue() string { + return uspor.Value +} diff --git a/sections/uspor/uspor_test.go b/sections/uspor/uspor_test.go new file mode 100644 index 0000000..b5accdb --- /dev/null +++ b/sections/uspor/uspor_test.go @@ -0,0 +1,62 @@ +package uspor + +import ( + "github.com/prebid/go-gpp/sections" + "testing" + + "github.com/prebid/go-gpp/constants" + "github.com/stretchr/testify/assert" +) + +type usporTestData struct { + description string + gppString string + expected USPOR +} + +func TestUSPOR(t *testing.T) { + testData := []usporTestData{ + { + description: "should populate USPOR segments correctly", + gppString: "bSFgmGACUA.YA", + /* + 011011 01 00 10 00 01 0110000010011000011000 000000 00 10 01 01 1100 01 1 + */ + expected: USPOR{ + CoreSegment: USPORCoreSegment{ + Version: 27, + ProcessingNotice: 1, + SaleOptOutNotice: 0, + TargetedAdvertisingOptOutNotice: 2, + SaleOptOut: 0, + TargetedAdvertisingOptOut: 1, + SensitiveDataProcessing: []byte{ + 1, 2, 0, 0, 2, 1, 2, 0, 1, 2, 0, + }, + KnownChildSensitiveDataConsents: []byte{0, 0, 0}, + AdditionalDataProcessingConsent: 0, + MspaCoveredTransaction: 2, + MspaOptOutOptionMode: 1, + MspaServiceProviderMode: 1, + }, + SectionID: constants.SectionUSPOR, + Value: "bSFgmGACUA.YA", + GPCSegment: sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: true, + }, + }, + }, + } + + for _, test := range testData { + result, err := NewUSPOR(test.gppString) + encodedString := string(test.expected.Encode(true)) + + assert.Nil(t, err) + assert.Equal(t, test.expected, result) + assert.Equal(t, constants.SectionUSPOR, result.GetID()) + assert.Equal(t, test.gppString, result.GetValue()) + assert.Equal(t, test.gppString, encodedString) + } +} diff --git a/sections/usptx/usptx_test.go b/sections/usptx/usptx_test.go new file mode 100644 index 0000000..59810cd --- /dev/null +++ b/sections/usptx/usptx_test.go @@ -0,0 +1,60 @@ +package usptx + +import ( + "testing" + + "github.com/prebid/go-gpp/constants" + "github.com/prebid/go-gpp/sections" + "github.com/stretchr/testify/assert" +) + +type usptxTestData struct { + description string + gppString string + expected USPTX +} + +func TestUSPTX(t *testing.T) { + testData := []usptxTestData{ + { + description: "should populate USPTX segments correctly", + gppString: "bVVVVVVA", + /* + 011011 01 01 01 01 01 01 0101010101010101 01 01 01 01 01 10000 + */ + expected: USPTX{ + CoreSegment: USPTXCoreSegment{ + Version: 27, + ProcessingNotice: 1, + SaleOptOutNotice: 1, + TargetedAdvertisingOptOutNotice: 1, + SaleOptOut: 1, + TargetedAdvertisingOptOut: 1, + SensitiveDataProcessing: []byte{1, 1, 1, 1, 1, 1, 1, 1}, + KnownChildSensitiveDataConsents: 1, + AdditionalDataProcessingConsent: 1, + MspaCoveredTransaction: 1, + MspaOptOutOptionMode: 1, + MspaServiceProviderMode: 1, + }, + SectionID: constants.SectionUSPTX, + Value: "bVVVVVVA", + GPCSegment: sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: false, + }, + }, + }, + } + + for _, test := range testData { + result, err := NewUSPTX(test.gppString) + encodedString := string(test.expected.Encode(false)) + + assert.Nil(t, err) + assert.Equal(t, test.expected, result) + assert.Equal(t, constants.SectionUSPTX, result.GetID()) + assert.Equal(t, test.gppString, result.GetValue()) + assert.Equal(t, test.gppString, encodedString) + } +} diff --git a/sections/usptx/uxptx.go b/sections/usptx/uxptx.go new file mode 100644 index 0000000..94c45ef --- /dev/null +++ b/sections/usptx/uxptx.go @@ -0,0 +1,166 @@ +package usptx + +import ( + "github.com/prebid/go-gpp/constants" + "github.com/prebid/go-gpp/sections" + "github.com/prebid/go-gpp/util" +) + +type USPTXCoreSegment struct { + Version byte + ProcessingNotice byte + SaleOptOutNotice byte + TargetedAdvertisingOptOutNotice byte + SaleOptOut byte + TargetedAdvertisingOptOut byte + SensitiveDataProcessing []byte + KnownChildSensitiveDataConsents byte + AdditionalDataProcessingConsent byte + MspaCoveredTransaction byte + MspaOptOutOptionMode byte + MspaServiceProviderMode byte +} + +type USPTX struct { + SectionID constants.SectionID + Value string + CoreSegment USPTXCoreSegment + GPCSegment sections.CommonUSGPCSegment +} + +func NewUPSTXCoreSegment(bs *util.BitStream) (USPTXCoreSegment, error) { + var usptxCore USPTXCoreSegment + var err error + + usptxCore.Version, err = bs.ReadByte6() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.Version", err) + } + + usptxCore.ProcessingNotice, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.ProcessingNotice", err) + } + + usptxCore.SaleOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.SaleOptOutNotice", err) + } + + usptxCore.TargetedAdvertisingOptOutNotice, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.TargetedAdvertisingOptOutNotice", err) + } + + usptxCore.SaleOptOut, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.SaleOptOut", err) + } + + usptxCore.TargetedAdvertisingOptOut, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.TargetedAdvertisingOptOut", err) + } + + usptxCore.SensitiveDataProcessing, err = bs.ReadTwoBitField(8) + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.SensitiveDataProcessing", err) + } + + usptxCore.KnownChildSensitiveDataConsents, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.KnownChildSensitiveDataConsents", err) + } + + usptxCore.AdditionalDataProcessingConsent, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.AdditionalDataProcessingConsent", err) + } + + usptxCore.MspaCoveredTransaction, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.MspaCoveredTransaction", err) + } + + usptxCore.MspaOptOutOptionMode, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.MspaOptOutOptionMode", err) + } + + usptxCore.MspaServiceProviderMode, err = bs.ReadByte2() + if err != nil { + return usptxCore, sections.ErrorHelper("CoreSegment.MspaServiceProviderMode", err) + } + + return usptxCore, nil +} + +func (segment USPTXCoreSegment) Encode(bs *util.BitStream) { + bs.WriteByte6(segment.Version) + bs.WriteByte2(segment.ProcessingNotice) + bs.WriteByte2(segment.SaleOptOutNotice) + bs.WriteByte2(segment.TargetedAdvertisingOptOutNotice) + bs.WriteByte2(segment.SaleOptOut) + bs.WriteByte2(segment.TargetedAdvertisingOptOut) + bs.WriteTwoBitField(segment.SensitiveDataProcessing) + bs.WriteByte2(segment.KnownChildSensitiveDataConsents) + bs.WriteByte2(segment.AdditionalDataProcessingConsent) + bs.WriteByte2(segment.MspaCoveredTransaction) + bs.WriteByte2(segment.MspaOptOutOptionMode) + bs.WriteByte2(segment.MspaServiceProviderMode) +} + +func NewUSPTX(encoded string) (USPTX, error) { + usptx := USPTX{} + + bitStream, gpcBitStream, err := sections.CreateBitStreams(encoded, true) + if err != nil { + return usptx, err + } + + coreSegment, err := NewUPSTXCoreSegment(bitStream) + if err != nil { + return usptx, err + } + + gpcSegment := sections.CommonUSGPCSegment{ + SubsectionType: 1, + Gpc: false, + } + + if gpcBitStream != nil { + gpcSegment, err = sections.NewCommonUSGPCSegment(gpcBitStream) + if err != nil { + return usptx, err + } + } + usptx = USPTX{ + SectionID: constants.SectionUSPTX, + Value: encoded, + CoreSegment: coreSegment, + GPCSegment: gpcSegment, + } + + return usptx, nil +} + +func (usptx USPTX) Encode(gpcIncluded bool) []byte { + bs := util.NewBitStreamForWrite() + usptx.CoreSegment.Encode(bs) + res := bs.Base64Encode() + if !gpcIncluded { + return res + } + bs.Reset() + res = append(res, '.') + usptx.GPCSegment.Encode(bs) + return append(res, bs.Base64Encode()...) +} + +func (usput USPTX) GetID() constants.SectionID { + return usput.SectionID +} + +func (usput USPTX) GetValue() string { + return usput.Value +}