Skip to content

Commit

Permalink
Merge pull request #2 from zattoo/4.2-extensions
Browse files Browse the repository at this point in the history
Add AdVerification Extension
  • Loading branch information
ivan-kosolapov-zattoo authored Jul 24, 2024
2 parents d16b659 + bc714ed commit e9ad5ce
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 14 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Upload Go test results

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.13', '1.20', '1.21' ]

steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Install dependencies
run: go get .

- name: Test with Go
run: go test -json > TestResults-${{ matrix.go-version }}.json

- name: Upload Go test results
uses: actions/upload-artifact@v4
with:
name: Go-results-${{ matrix.go-version }}
path: TestResults-${{ matrix.go-version }}.json
25 changes: 15 additions & 10 deletions extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import "encoding/xml"
type Extension struct {
Type string `xml:"type,attr,omitempty"`
CustomTracking []Tracking `xml:"CustomTracking>Tracking,omitempty" json:",omitempty"`
Data string `xml:",innerxml" json:",omitempty"`
// AdVerifications are IAB Open Measurement tags backported to VAST 2 and 3 as an extension
AdVerifications *[]Verification `xml:"AdVerifications>Verification,omitempty" json:",omitempty"`
Data string `xml:",innerxml" json:",omitempty"`
}

// the extension type as a middleware in the encoding process.
type extension Extension

type extensionNoCT struct {
type extensionOnlyData struct {
Type string `xml:"type,attr,omitempty"`
Data string `xml:",innerxml" json:",omitempty"`
}
Expand All @@ -23,12 +25,12 @@ func (e Extension) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
// create a temporary element from a wrapper Extension, copy what we need to
// it and return it's encoding.
var e2 interface{}
// if we have custom trackers, we should ignore the data, if not, then we
// should consider only the data.
if len(e.CustomTracking) > 0 {
e2 = extension{Type: e.Type, CustomTracking: e.CustomTracking}
// if we have custom trackers or ad verifications, we should ignore the data, if not, then we
// should consider only the data
if len(e.CustomTracking) == 0 && (e.AdVerifications == nil || len(*e.AdVerifications) == 0) {
e2 = extensionOnlyData{Type: e.Type, Data: e.Data}
} else {
e2 = extensionNoCT{Type: e.Type, Data: e.Data}
e2 = extension{Type: e.Type, CustomTracking: e.CustomTracking, AdVerifications: e.AdVerifications}
}

return enc.EncodeElement(e2, start)
Expand All @@ -42,11 +44,14 @@ func (e *Extension) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error
if err := dec.DecodeElement(&e2, &start); err != nil {
return err
}
// copy the type and the customTracking

// copy the type, customTracking and adVerifications
e.Type = e2.Type
e.CustomTracking = e2.CustomTracking
// copy the data only of customTracking is empty
if len(e.CustomTracking) == 0 {
e.AdVerifications = e2.AdVerifications

// copy the data only if customTracking and adVerifications are empty
if len(e.CustomTracking) == 0 && (e.AdVerifications == nil || len(*e.AdVerifications) == 0) {
e.Data = e2.Data
}
return nil
Expand Down
87 changes: 87 additions & 0 deletions extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package vast

import (
"encoding/xml"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

var (
extensionCustomTracking = []byte(`<Extension type="testCustomTracking"><CustomTracking><Tracking event="event.1"><![CDATA[http://event.1]]></Tracking><Tracking event="event.2"><![CDATA[http://event.2]]></Tracking></CustomTracking></Extension>`)
extensionAdVerification = []byte(`<Extension type="AdVerifications"><AdVerifications><Verification vendor="doubleclickbygoogle.com-omid-video"><JavaScriptResource apiFramework="omid" browserOptional="true"><![CDATA[https://example.com/verify.js]]></JavaScriptResource><VerificationParameters><![CDATA[example=1&param=2]]></VerificationParameters><TrackingEvents><Tracking event="verificationNotExecuted"><![CDATA[https://pagead2.googlesyndication.com/pagead/interaction/?ai=Bt7src9CCZofvMqChiM0Pi8qQkAPFnbOVRgAAABABII64hW84AVjUt8DBgwRglfrwgYwHsgETZ29vZ2xlYWRzLmdpdGh1Yi5pb7oBCjcyOHg5MF94bWzIAQXaATRodHRwczovL2dvb2dsZWFkcy5naXRodWIuaW8vZ29vZ2xlYWRzLWltYS1odG1sNS92c2kvwAIC4AIA6gIlLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3V0aWxpdHlfc2FtcGxlc_gC8NEegAMBkAPIBpgD4AOoAwHgBAHSBQYQj6GjiRagBiOoB7i-sQKoB5oGqAfz0RuoB5bYG6gHqpuxAqgHg62xAqgH4L2xAqgH_56xAqgH35-xAqgH-MKxAqgH-8KxAtgHAdIIMQiR4YBwEAEYHTIH64uA7r-AAToPgNCAgICAhAiAgICAgJQuSL39wTpY1cHtiZmGhwPYCAKACgWYCwGqDQJERdAVAfgWAYAXAQ&sigh=UTbooye19j8&label=active_view_verification_rejected&errorcode=%5BREASON%5D]]></Tracking></TrackingEvents></Verification></AdVerifications></Extension>`)
extensionData = []byte(`<Extension type="testCustomTracking"><SkippableAdType>Generic</SkippableAdType></Extension>`)

multipleExtensions = []byte(fmt.Sprintf(`<InLine><Extensions>%s%s%s</Extensions></InLine>`, extensionCustomTracking, extensionAdVerification, extensionData))
)

func TestExtensionCustomTrackingMarshal(t *testing.T) {
Expand Down Expand Up @@ -60,6 +64,32 @@ func TestExtensionCustomTracking(t *testing.T) {
assert.Equal(t, string(extensionCustomTracking), string(xmlExtensionOutput))
}

func TestExtensionCustomAdVerification(t *testing.T) {
// unmarshal the Extension
var e Extension
assert.NoError(t, xml.Unmarshal(extensionAdVerification, &e))

// assert the resulting extension
assert.Equal(t, "AdVerifications", e.Type)
assert.Empty(t, e.Data)
if assert.NotNil(t, e.AdVerifications) && assert.Len(t, *e.AdVerifications, 1) {
assert.Equal(t, "doubleclickbygoogle.com-omid-video", (*e.AdVerifications)[0].Vendor)
if assert.Len(t, (*e.AdVerifications)[0].JavaScriptResource, 1) {
assert.Equal(t, JavaScriptResource{
ApiFramework: "omid",
BrowserOptional: true,
URI: "https://example.com/verify.js",
}, (*e.AdVerifications)[0].JavaScriptResource[0])
}
if assert.Len(t, (*e.AdVerifications)[0].TrackingEvents, 1) {
assert.Equal(t, Tracking{
Event: "verificationNotExecuted",
URI: "https://pagead2.googlesyndication.com/pagead/interaction/?ai=Bt7src9CCZofvMqChiM0Pi8qQkAPFnbOVRgAAABABII64hW84AVjUt8DBgwRglfrwgYwHsgETZ29vZ2xlYWRzLmdpdGh1Yi5pb7oBCjcyOHg5MF94bWzIAQXaATRodHRwczovL2dvb2dsZWFkcy5naXRodWIuaW8vZ29vZ2xlYWRzLWltYS1odG1sNS92c2kvwAIC4AIA6gIlLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3V0aWxpdHlfc2FtcGxlc_gC8NEegAMBkAPIBpgD4AOoAwHgBAHSBQYQj6GjiRagBiOoB7i-sQKoB5oGqAfz0RuoB5bYG6gHqpuxAqgHg62xAqgH4L2xAqgH_56xAqgH35-xAqgH-MKxAqgH-8KxAtgHAdIIMQiR4YBwEAEYHTIH64uA7r-AAToPgNCAgICAhAiAgICAgJQuSL39wTpY1cHtiZmGhwPYCAKACgWYCwGqDQJERdAVAfgWAYAXAQ&sigh=UTbooye19j8&label=active_view_verification_rejected&errorcode=%5BREASON%5D",
}, (*e.AdVerifications)[0].TrackingEvents[0])
}
}
}

func TestExtensionGeneric(t *testing.T) {
// unmarshal the Extension
var e Extension
Expand All @@ -77,3 +107,60 @@ func TestExtensionGeneric(t *testing.T) {
// assert the resulting marshaled extension
assert.Equal(t, string(extensionData), string(xmlExtensionOutput))
}

func TestMultipleExtensions(t *testing.T) {
// unmarshal the Extensions
var inline InLine
assert.NoError(t, xml.Unmarshal(multipleExtensions, &inline))

extensions := *inline.Extensions

// Check each extension
if assert.Len(t, extensions, 3) {
// Custom tracking
{
e := extensions[0]
assert.Equal(t, "testCustomTracking", e.Type)
assert.Empty(t, string(e.Data))
if assert.Len(t, e.CustomTracking, 2) {
// first event
assert.Equal(t, "event.1", e.CustomTracking[0].Event)
assert.Equal(t, "http://event.1", e.CustomTracking[0].URI)
// second event
assert.Equal(t, "event.2", e.CustomTracking[1].Event)
assert.Equal(t, "http://event.2", e.CustomTracking[1].URI)
}
}

// Ad verifications
{
e := extensions[1]
assert.Equal(t, "AdVerifications", e.Type)
assert.Empty(t, e.Data)
if assert.NotNil(t, e.AdVerifications) && assert.Len(t, *e.AdVerifications, 1) {
assert.Equal(t, "doubleclickbygoogle.com-omid-video", (*e.AdVerifications)[0].Vendor)
if assert.Len(t, (*e.AdVerifications)[0].JavaScriptResource, 1) {
assert.Equal(t, JavaScriptResource{
ApiFramework: "omid",
BrowserOptional: true,
URI: "https://example.com/verify.js",
}, (*e.AdVerifications)[0].JavaScriptResource[0])
}
if assert.Len(t, (*e.AdVerifications)[0].TrackingEvents, 1) {
assert.Equal(t, Tracking{
Event: "verificationNotExecuted",
URI: "https://pagead2.googlesyndication.com/pagead/interaction/?ai=Bt7src9CCZofvMqChiM0Pi8qQkAPFnbOVRgAAABABII64hW84AVjUt8DBgwRglfrwgYwHsgETZ29vZ2xlYWRzLmdpdGh1Yi5pb7oBCjcyOHg5MF94bWzIAQXaATRodHRwczovL2dvb2dsZWFkcy5naXRodWIuaW8vZ29vZ2xlYWRzLWltYS1odG1sNS92c2kvwAIC4AIA6gIlLzIxNzc1NzQ0OTIzL2V4dGVybmFsL3V0aWxpdHlfc2FtcGxlc_gC8NEegAMBkAPIBpgD4AOoAwHgBAHSBQYQj6GjiRagBiOoB7i-sQKoB5oGqAfz0RuoB5bYG6gHqpuxAqgHg62xAqgH4L2xAqgH_56xAqgH35-xAqgH-MKxAqgH-8KxAtgHAdIIMQiR4YBwEAEYHTIH64uA7r-AAToPgNCAgICAhAiAgICAgJQuSL39wTpY1cHtiZmGhwPYCAKACgWYCwGqDQJERdAVAfgWAYAXAQ&sigh=UTbooye19j8&label=active_view_verification_rejected&errorcode=%5BREASON%5D",
}, (*e.AdVerifications)[0].TrackingEvents[0])
}
}
}

// Generic
{
e := extensions[2]
assert.Equal(t, "testCustomTracking", e.Type)
assert.Equal(t, "<SkippableAdType>Generic</SkippableAdType>", string(e.Data))
assert.Empty(t, e.CustomTracking)
}
}
}
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
13 changes: 9 additions & 4 deletions vast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"github.com/pquerna/ffjson/ffjson"
"io/ioutil"
"os"
"reflect"
"testing"
"time"

"github.com/pquerna/ffjson/ffjson"
)

func TestQuickStartComplex(t *testing.T) {
skip := Duration(5 * time.Second)
v := VAST{
XMLName: xml.Name{Local: "VAST"},
Version: "4.2",
Ads: []Ad{
{
Expand Down Expand Up @@ -113,6 +115,7 @@ func TestQuickStartComplex(t *testing.T) {
func TestQuickStart(t *testing.T) {
d := Duration(5 * time.Second)
v := VAST{
XMLName: xml.Name{Local: "VAST"},
Mute: true,
Version: "3.0",
Ads: []Ad{
Expand Down Expand Up @@ -172,7 +175,7 @@ func TestQuickStart(t *testing.T) {
},
}

want := []byte(`{"Version":"3.0","Ad":[{"ID":"123","Type":"front","InLine":{"AdSystem":{"Data":"DSP"},"AdTitle":{"Data":"adTitle"},"Impressions":[{"ID":"11111","URI":"http://impressionv1.track.com"},{"ID":"11112","URI":"http://impressionv2.track.com"}],"Creatives":[{"ID":"987","Linear":{"SkipOffset":"00:00:05","Duration":"00:00:15","TrackingEvents":[{"Event":"start","URI":"http://track.xxx.com/q/start?xx"},{"Event":"firstQuartile","URI":"http://track.xxx.com/q/firstQuartile?xx"},{"Event":"midpoint","URI":"http://track.xxx.com/q/midpoint?xx"},{"Event":"thirdQuartile","URI":"http://track.xxx.com/q/thirdQuartile?xx"},{"Event":"complete","URI":"http://track.xxx.com/q/complete?xx"}],"MediaFiles":{"MediaFile":[{"Delivery":"progressive","Type":"video/mp4","Width":1024,"Height":576,"URI":"http://mp4.res.xxx.com/new_video/2020/01/14/1485/335928CBA9D02E95E63ED9F4D45DF6DF_20200114_1_1_1051.mp4","Label":"123"}]}}}],"Extensions":[{"Type":"ClassName","Data":"AdsVideoView"},{"Type":"ExtURL","Data":"http://xxxxxxxx"}]}}],"Mute":true}`)
want := []byte(`{"XMLName":{"Space":"","Local":"VAST"},"Version":"3.0","Ad":[{"ID":"123","Type":"front","InLine":{"AdSystem":{"Data":"DSP"},"AdTitle":{"Data":"adTitle"},"Impressions":[{"ID":"11111","URI":"http://impressionv1.track.com"},{"ID":"11112","URI":"http://impressionv2.track.com"}],"Creatives":[{"ID":"987","Linear":{"SkipOffset":"00:00:05","Duration":"00:00:15","TrackingEvents":[{"Event":"start","URI":"http://track.xxx.com/q/start?xx"},{"Event":"firstQuartile","URI":"http://track.xxx.com/q/firstQuartile?xx"},{"Event":"midpoint","URI":"http://track.xxx.com/q/midpoint?xx"},{"Event":"thirdQuartile","URI":"http://track.xxx.com/q/thirdQuartile?xx"},{"Event":"complete","URI":"http://track.xxx.com/q/complete?xx"}],"MediaFiles":{"MediaFile":[{"Delivery":"progressive","Type":"video/mp4","Width":1024,"Height":576,"URI":"http://mp4.res.xxx.com/new_video/2020/01/14/1485/335928CBA9D02E95E63ED9F4D45DF6DF_20200114_1_1_1051.mp4","Label":"123"}]}}}],"Extensions":[{"Type":"ClassName","Data":"AdsVideoView"},{"Type":"ExtURL","Data":"http://xxxxxxxx"}]}}],"Mute":true}`)
got, err := json.Marshal(v)
t.Logf("%s", got)
if err != nil {
Expand All @@ -187,12 +190,13 @@ func TestQuickStart(t *testing.T) {

func TestEmptyVast(t *testing.T) {
v := VAST{
XMLName: xml.Name{Local: "VAST"},
Version: "3.0",
Errors: []CDATAString{
{CDATA: "http://xx.xx.com/e/error?e=__ERRORCODE__&co=__CONTENTPLAYHEAD__&ca=__CACHEBUSTING__&a=__ASSETURI__&t=__TIMESTAMP__&o=__OTHER__"},
},
}
want := []byte(`{"Version":"3.0","Errors":[{"Data":"http://xx.xx.com/e/error?e=__ERRORCODE__\u0026co=__CONTENTPLAYHEAD__\u0026ca=__CACHEBUSTING__\u0026a=__ASSETURI__\u0026t=__TIMESTAMP__\u0026o=__OTHER__"}]}`)
want := []byte(`{"XMLName":{"Space":"","Local":"VAST"},"Version":"3.0","Errors":[{"Data":"http://xx.xx.com/e/error?e=__ERRORCODE__\u0026co=__CONTENTPLAYHEAD__\u0026ca=__CACHEBUSTING__\u0026a=__ASSETURI__\u0026t=__TIMESTAMP__\u0026o=__OTHER__"}]}`)
got, err := json.Marshal(v)
if err != nil {
t.Errorf("Marshal() error = %v", err)
Expand Down Expand Up @@ -227,6 +231,7 @@ func createVastDemo() (*VAST, error) {
mediaURI := "http://mp4.res.xxx.com/new_video/2020/01/14/1485/335928CBA9D02E95E63ED9F4D45DF6DF_20200114_1_1_1051.mp4"

v := &VAST{
XMLName: xml.Name{Local: "VAST"},
Version: "3.0",
Ads: []Ad{
{
Expand Down Expand Up @@ -318,7 +323,7 @@ func TestCreateVastJson(t *testing.T) {
want []byte
wantErr bool
}{
{name: "testCase1", want: []byte(`{"Version":"3.0","Ad":[{"ID":"123","Type":"front","InLine":{"AdSystem":{"Data":"DSP"},"AdTitle":{"Data":"ad title"},"Impressions":[{"ID":"456","URI":"http://impression.track.cn"}],"Creatives":[{"ID":"123456","Linear":{"Duration":"00:00:15","TrackingEvents":[{"Event":"start","URI":"http://track.xxx.com/q/start?xx"}],"MediaFiles":{"MediaFile":[{"Delivery":"progressive","Type":"video/mp4","Width":1024,"Height":576,"URI":"http://mp4.res.xxx.com/new_video/2020/01/14/1485/335928CBA9D02E95E63ED9F4D45DF6DF_20200114_1_1_1051.mp4","Label":"123"}]}}}]}}]}`),
{name: "testCase1", want: []byte(`{"XMLName":{"Space":"","Local":"VAST"},"Version":"3.0","Ad":[{"ID":"123","Type":"front","InLine":{"AdSystem":{"Data":"DSP"},"AdTitle":{"Data":"ad title"},"Impressions":[{"ID":"456","URI":"http://impression.track.cn"}],"Creatives":[{"ID":"123456","Linear":{"Duration":"00:00:15","TrackingEvents":[{"Event":"start","URI":"http://track.xxx.com/q/start?xx"}],"MediaFiles":{"MediaFile":[{"Delivery":"progressive","Type":"video/mp4","Width":1024,"Height":576,"URI":"http://mp4.res.xxx.com/new_video/2020/01/14/1485/335928CBA9D02E95E63ED9F4D45DF6DF_20200114_1_1_1051.mp4","Label":"123"}]}}}]}}]}`),
wantErr: false},
}
for _, tt := range tests {
Expand Down

0 comments on commit e9ad5ce

Please sign in to comment.