From 22eefd1e87c6e2abaca3fe509cdaf7a3db5e466f Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 20 Mar 2024 17:15:03 -0700 Subject: [PATCH 1/7] Fix concurrent map property access --- pkg/fetcher/fetcher_archive.go | 8 +-- pkg/fetcher/fetcher_archive_test.go | 33 +++++----- pkg/manifest/link.go | 44 ++++++++++++- pkg/manifest/link_test.go | 48 +++++++------- pkg/manifest/properties.go | 97 ++++++++++++++++++++++----- pkg/manifest/properties_test.go | 99 ++++++++++++++++++++++------ pkg/parser/epub/deobfuscator_test.go | 2 +- pkg/parser/epub/factory.go | 8 +-- pkg/parser/epub/positions_service.go | 2 +- pkg/streamer/a11y_infer_test.go | 4 +- 10 files changed, 257 insertions(+), 88 deletions(-) diff --git a/pkg/fetcher/fetcher_archive.go b/pkg/fetcher/fetcher_archive.go index c84147b1..eb1efea0 100644 --- a/pkg/fetcher/fetcher_archive.go +++ b/pkg/fetcher/fetcher_archive.go @@ -40,8 +40,8 @@ func (f *ArchiveFetcher) Links() (manifest.LinkList, error) { if cl == 0 { cl = af.Length() } - link.Properties.Add(manifest.Properties{ - "https://readium.org/webpub-manifest/properties#archive": manifest.Properties{ + link.Properties.Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": cl, "isEntryCompressed": af.CompressedLength() > 0, }, @@ -109,8 +109,8 @@ func (r *entryResource) Link() manifest.Link { if cl == 0 { cl = r.entry.Length() } - r.link.Properties.Add(manifest.Properties{ - "https://readium.org/webpub-manifest/properties#archive": manifest.Properties{ + r.link.Properties.Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": cl, "isEntryCompressed": r.entry.CompressedLength() > 0, }, diff --git a/pkg/fetcher/fetcher_archive_test.go b/pkg/fetcher/fetcher_archive_test.go index 72198030..9c7a3c8a 100644 --- a/pkg/fetcher/fetcher_archive_test.go +++ b/pkg/fetcher/fetcher_archive_test.go @@ -16,16 +16,17 @@ func withArchiveFetcher(t *testing.T, callback func(a *ArchiveFetcher)) { func TestArchiveFetcherLinks(t *testing.T) { makeTestLink := func(href string, typ string, entryLength uint64, isCompressed bool) manifest.Link { - return manifest.Link{ + l := manifest.Link{ Href: href, Type: typ, - Properties: manifest.Properties{ - "https://readium.org/webpub-manifest/properties#archive": manifest.Properties{ - "entryLength": entryLength, - "isEntryCompressed": isCompressed, - }, - }, } + l.Properties.Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ + "entryLength": entryLength, + "isEntryCompressed": isCompressed, + }, + }) + return l } mustContain := manifest.LinkList{ @@ -127,26 +128,26 @@ func TestArchiveFetcherFileNotFoundLength(t *testing.T) { func TestArchiveFetcherAddsProperties(t *testing.T) { withArchiveFetcher(t, func(a *ArchiveFetcher) { resource := a.Get(manifest.Link{Href: "/EPUB/css/epub.css"}) - assert.Equal(t, manifest.Properties{ - "https://readium.org/webpub-manifest/properties#archive": manifest.Properties{ + assert.Equal(t, (&manifest.Properties{}).Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": uint64(595), "isEntryCompressed": true, }, - }, resource.Link().Properties) + }), resource.Link().Properties) }) } func TestArchiveFetcherOriginalPropertiesKept(t *testing.T) { withArchiveFetcher(t, func(a *ArchiveFetcher) { - resource := a.Get(manifest.Link{Href: "/EPUB/css/epub.css", Properties: manifest.Properties{ - "other": "property", - }}) - assert.Equal(t, manifest.Properties{ + l := manifest.Link{Href: "/EPUB/css/epub.css"} + l.Properties.Add(map[string]interface{}{"other": "property"}) + resource := a.Get(l) + assert.Equal(t, (&manifest.Properties{}).Add(map[string]interface{}{ "other": "property", - "https://readium.org/webpub-manifest/properties#archive": manifest.Properties{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": uint64(595), "isEntryCompressed": true, }, - }, resource.Link().Properties) + }), resource.Link().Properties) }) } diff --git a/pkg/manifest/link.go b/pkg/manifest/link.go index 5439e58c..76d9a470 100644 --- a/pkg/manifest/link.go +++ b/pkg/manifest/link.go @@ -107,7 +107,7 @@ func LinkFromJSON(rawJson map[string]interface{}, normalizeHref LinkHrefNormaliz // Properties properties, ok := rawJson["properties"].(map[string]interface{}) if ok { - link.Properties = properties + link.Properties.Add(properties) } // Rels @@ -181,6 +181,48 @@ func (l *Link) UnmarshalJSON(b []byte) error { return nil } +func (l Link) MarshalJSON() ([]byte, error) { + res := make(map[string]interface{}) + res["href"] = l.Href + if l.Type != "" { + res["type"] = l.Type + } + if l.Templated { + res["templated"] = l.Templated + } + if l.Title != "" { + res["title"] = l.Title + } + if len(l.Rels) > 0 { + res["rel"] = l.Rels + } + if l.Properties.Length() > 0 { + res["properties"] = l.Properties + } + if l.Height > 0 { + res["height"] = l.Height + } + if l.Width > 0 { + res["width"] = l.Width + } + if l.Bitrate > 0 { + res["bitrate"] = l.Bitrate + } + if l.Duration > 0 { + res["duration"] = l.Duration + } + if len(l.Languages) > 0 { + res["language"] = l.Languages + } + if len(l.Alternates) > 0 { + res["alternate"] = l.Alternates + } + if len(l.Children) > 0 { + res["children"] = l.Children + } + return json.Marshal(res) +} + // Slice of links type LinkList []Link diff --git a/pkg/manifest/link_test.go b/pkg/manifest/link_test.go index 12ad8b43..78e3432a 100644 --- a/pkg/manifest/link_test.go +++ b/pkg/manifest/link_test.go @@ -66,17 +66,19 @@ func TestLinkUnmarshalFullJSON(t *testing.T) { ] }`), &l)) assert.Equal(t, Link{ - Href: "http://href", - Type: "application/pdf", - Templated: true, - Title: "Link Title", - Rels: []string{"publication", "cover"}, - Properties: map[string]interface{}{"orientation": "landscape"}, - Height: 1024, - Width: 768, - Bitrate: 74.2, - Duration: 45.6, - Languages: []string{"fr"}, + Href: "http://href", + Type: "application/pdf", + Templated: true, + Title: "Link Title", + Rels: []string{"publication", "cover"}, + Properties: (&Properties{}).Add(map[string]interface{}{ + "orientation": "landscape", + }), + Height: 1024, + Width: 768, + Bitrate: 74.2, + Duration: 45.6, + Languages: []string{"fr"}, Alternates: []Link{ {Href: "/alternate1"}, {Href: "/alternate2"}, @@ -181,17 +183,19 @@ func TestLinkMinimalJSON(t *testing.T) { func TestLinkFullJSON(t *testing.T) { b, err := json.Marshal(Link{ - Href: "http://href", - Type: "application/pdf", - Templated: true, - Title: "Link Title", - Rels: []string{"publication", "cover"}, - Properties: map[string]interface{}{"orientation": "landscape"}, - Height: 1024, - Width: 768, - Bitrate: 74.2, - Duration: 45.6, - Languages: []string{"fr"}, + Href: "http://href", + Type: "application/pdf", + Templated: true, + Title: "Link Title", + Rels: []string{"publication", "cover"}, + Properties: (&Properties{}).Add(map[string]interface{}{ + "orientation": "landscape", + }), + Height: 1024, + Width: 768, + Bitrate: 74.2, + Duration: 45.6, + Languages: []string{"fr"}, Alternates: []Link{ {Href: "/alternate1"}, {Href: "/alternate2"}, diff --git a/pkg/manifest/properties.go b/pkg/manifest/properties.go index 60a0e3fc..d8ccd71b 100644 --- a/pkg/manifest/properties.go +++ b/pkg/manifest/properties.go @@ -2,34 +2,76 @@ package manifest import ( "encoding/json" + "sync" "github.com/pkg/errors" ) -type Properties map[string]interface{} +type Properties struct { + properties map[string]interface{} + mutext *sync.RWMutex +} -func (p *Properties) Add(newProperties Properties) Properties { - if *p == nil { - *p = make(Properties) +func (p *Properties) Add(newProperties map[string]interface{}) Properties { + if p == nil { + p = &Properties{} } + if p.properties == nil || p.mutext == nil { + p.properties = make(map[string]interface{}) + p.mutext = &sync.RWMutex{} + } + p.mutext.Lock() + defer p.mutext.Unlock() + for k, v := range newProperties { - (*p)[k] = v + p.properties[k] = v } return *p } -func (p *Properties) Get(key string) interface{} { - if p != nil { - return (*p)[key] +func (p *Properties) Delete(key string) Properties { + if p == nil { + p = &Properties{} + } + if p.properties == nil || p.mutext == nil { + p.properties = make(map[string]interface{}) + p.mutext = &sync.RWMutex{} + } + p.mutext.Lock() + defer p.mutext.Unlock() + + delete(p.properties, key) + return *p +} + +func (p Properties) Get(key string) interface{} { + if p.properties != nil && p.mutext != nil { + p.mutext.RLock() + defer p.mutext.RUnlock() + + return p.properties[key] } return nil } +func (p Properties) Length() int { + if p.properties == nil || p.mutext == nil { + return 0 + } + p.mutext.RLock() + defer p.mutext.RUnlock() + + return len(p.properties) +} + func (p Properties) GetString(key string) string { - if p == nil { + if p.properties == nil || p.mutext == nil { return "" } - v, ok := p[key] + p.mutext.RLock() + defer p.mutext.RUnlock() + + v, ok := p.properties[key] if !ok { return "" } @@ -41,10 +83,13 @@ func (p Properties) GetString(key string) string { } func (p Properties) GetBool(key string) *bool { - if p == nil { + if p.properties == nil || p.mutext == nil { return nil } - v, ok := p[key] + p.mutext.RLock() + defer p.mutext.RUnlock() + + v, ok := p.properties[key] if !ok { return nil } @@ -104,10 +149,13 @@ func (p Properties) Encryption() *Encryption { } func (p Properties) Contains() []string { - if p == nil { + if p.properties == nil { return nil } - v, ok := p["contains"] + p.mutext.RLock() + defer p.mutext.RUnlock() + + v, ok := p.properties["contains"] if !ok { return nil } @@ -120,14 +168,20 @@ func (p Properties) Contains() []string { func PropertiesFromJSON(rawJson interface{}) (Properties, error) { if rawJson == nil { - return make(Properties), nil + return Properties{}, nil } properties, ok := rawJson.(map[string]interface{}) if !ok { - return nil, errors.New("Properties has invalid JSON object") + return Properties{}, errors.New("Properties has invalid JSON object") + } + if len(properties) > 0 { + return Properties{ + properties: properties, + mutext: &sync.RWMutex{}, + }, nil } - return properties, nil + return Properties{}, nil } func (p *Properties) UnmarshalJSON(data []byte) error { @@ -143,3 +197,12 @@ func (p *Properties) UnmarshalJSON(data []byte) error { *p = pr return nil } + +func (p Properties) MarshalJSON() ([]byte, error) { + if p.properties == nil || p.mutext == nil { + return json.Marshal(nil) + } + p.mutext.RLock() + defer p.mutext.RUnlock() + return json.Marshal(p.properties) +} diff --git a/pkg/manifest/properties_test.go b/pkg/manifest/properties_test.go index ebb920a1..0b1a2d53 100644 --- a/pkg/manifest/properties_test.go +++ b/pkg/manifest/properties_test.go @@ -2,6 +2,7 @@ package manifest import ( "encoding/json" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -27,21 +28,43 @@ func TestPropertiesUnmarshalFullJSON(t *testing.T) { }`), &p)) assert.Equal(t, Properties{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, + properties: map[string]interface{}{ + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, + }, + mutext: &sync.RWMutex{}, }, p) } +func TestPropertiesMarshalFullJSON(t *testing.T) { + p := Properties{ + properties: map[string]interface{}{ + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, + }, + mutext: &sync.RWMutex{}, + } + b, err := json.Marshal(p) + assert.NoError(t, err) + assert.JSONEq(t, `{"other-property1":"value","other-property2":[42]}`, string(b)) +} + func TestPropertiesAddGiven(t *testing.T) { p2 := Properties{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, + properties: map[string]interface{}{ + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, + }, + mutext: &sync.RWMutex{}, } assert.Equal(t, Properties{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, - "additional": "property", - }, p2.Add(Properties{ + properties: map[string]interface{}{ + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, + "additional": "property", + }, + mutext: &sync.RWMutex{}, + }, p2.Add(map[string]interface{}{ "additional": "property", })) } @@ -50,60 +73,96 @@ func TestPropertiesAddGiven(t *testing.T) { func TestPropertiesClippedAvailable(t *testing.T) { assert.Equal(t, true, *Properties{ - "clipped": true, + properties: map[string]interface{}{ + "clipped": true, + }, + mutext: &sync.RWMutex{}, }.Clipped(), "Clipped true when set to true") } func TestPropertiesClippedMissing(t *testing.T) { - assert.Nil(t, Properties{}.Clipped(), "Clipped nil when missing") + assert.Nil(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Clipped(), "Clipped nil when missing") } func TestPropertiesFitAvailable(t *testing.T) { assert.Equal(t, FitCover, Properties{ - "fit": "cover", + properties: map[string]interface{}{ + "fit": "cover", + }, + mutext: &sync.RWMutex{}, }.Fit(), "Fit cover when set to cover") } func TestPropertiesFitMissing(t *testing.T) { - assert.Empty(t, Properties{}.Clipped(), "Fit empty when missing") + assert.Empty(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Clipped(), "Fit empty when missing") } func TestPropertiesOrientationAvailable(t *testing.T) { assert.Equal(t, OrientationLandscape, Properties{ - "orientation": "landscape", + properties: map[string]interface{}{ + "orientation": "landscape", + }, + mutext: &sync.RWMutex{}, }.Orientation(), "Orientation landscape when set to landscape") } func TestPropertiesOrientationMissing(t *testing.T) { - assert.Empty(t, Properties{}.Orientation(), "Orientation empty when missing") + assert.Empty(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Orientation(), "Orientation empty when missing") } func TestPropertiesOverflowAvailable(t *testing.T) { assert.Equal(t, OverflowScrolled, Properties{ - "overflow": "scrolled", + properties: map[string]interface{}{ + "overflow": "scrolled", + }, + mutext: &sync.RWMutex{}, }.Overflow(), "Overflow scrolled when set to scrolled") } func TestPropertiesOverflowMissing(t *testing.T) { - assert.Empty(t, Properties{}.Overflow(), "Overflow empty when missing") + assert.Empty(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Overflow(), "Overflow empty when missing") } func TestPropertiesPageAvailable(t *testing.T) { assert.Equal(t, PageRight, Properties{ - "page": "right", + properties: map[string]interface{}{ + "page": "right", + }, + mutext: &sync.RWMutex{}, }.Page(), "Page right when set to right") } func TestPropertiesPageMissing(t *testing.T) { - assert.Empty(t, Properties{}.Page(), "Page empty when missing") + assert.Empty(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Page(), "Page empty when missing") } func TestPropertiesSpreadAvailable(t *testing.T) { assert.Equal(t, SpreadBoth, Properties{ - "spread": "both", + properties: map[string]interface{}{ + "spread": "both", + }, + mutext: &sync.RWMutex{}, }.Spread(), "Spread both when set to both") } func TestPropertiesSpreadMissing(t *testing.T) { - assert.Empty(t, Properties{}.Spread(), "Spread empty when missing") + assert.Empty(t, Properties{ + properties: map[string]interface{}{}, + mutext: &sync.RWMutex{}, + }.Spread(), "Spread empty when missing") } diff --git a/pkg/parser/epub/deobfuscator_test.go b/pkg/parser/epub/deobfuscator_test.go index 0e88753f..41b9ce03 100644 --- a/pkg/parser/epub/deobfuscator_test.go +++ b/pkg/parser/epub/deobfuscator_test.go @@ -28,7 +28,7 @@ func withDeobfuscator(t *testing.T, href string, algorithm string, start, end in Href: href, } if algorithm != "" { - link.Properties.Add(manifest.Properties{ + link.Properties.Add(map[string]interface{}{ "encrypted": map[string]interface{}{ "algorithm": algorithm, }, diff --git a/pkg/parser/epub/factory.go b/pkg/parser/epub/factory.go index c6d13b27..47a78eab 100644 --- a/pkg/parser/epub/factory.go +++ b/pkg/parser/epub/factory.go @@ -136,9 +136,9 @@ func mapEPUBLink(link EPUBLink) manifest.Link { } if len(contains) > 0 { - l.Properties = manifest.Properties{ + l.Properties.Add(map[string]interface{}{ "contains": contains, - } + }) } return l @@ -190,7 +190,7 @@ func (f PublicationFactory) computeLink(item Item, fallbackChain []string) manif Alternates: f.computeAlternates(item, fallbackChain), } - if len(properties) > 0 { + if properties.Length() > 0 { ret.Properties = properties } @@ -231,7 +231,7 @@ func (f PublicationFactory) computePropertiesAndRels(item Item, itemref *ItemRef properties["encrypted"] = edat.ToMap() // ToMap makes it JSON-like } - return rels, manifest.Properties(properties) + return rels, (&manifest.Properties{}).Add(properties) } // Compute alternate links for [item], checking for an infinite recursion diff --git a/pkg/parser/epub/positions_service.go b/pkg/parser/epub/positions_service.go index 0da84cff..091105bb 100644 --- a/pkg/parser/epub/positions_service.go +++ b/pkg/parser/epub/positions_service.go @@ -173,7 +173,7 @@ func (l ArchiveEntryLength) PositionCount(resource fetcher.Resource) uint { var length uint64 props := resource.Link().Properties if p := props.Get("https://readium.org/webpub-manifest/properties#archive"); p != nil { - if pm, ok := p.(manifest.Properties); ok { + if pm, ok := p.(map[string]interface{}); ok { if el, ok := pm["entryLength"].(uint64); ok { length = el } diff --git a/pkg/streamer/a11y_infer_test.go b/pkg/streamer/a11y_infer_test.go index e391b454..b6fc94c1 100644 --- a/pkg/streamer/a11y_infer_test.go +++ b/pkg/streamer/a11y_infer_test.go @@ -212,9 +212,9 @@ func TestInferFeaturePageList(t *testing.T) { // "resources" in RWPM) func TestInferFeatureMathML(t *testing.T) { link := newLink(mediatype.HTML, "html") - link.Properties = map[string]interface{}{ + link.Properties.Add(map[string]interface{}{ "contains": []string{"mathml"}, - } + }) m := manifest.Manifest{ Metadata: manifest.Metadata{ ConformsTo: []manifest.Profile{manifest.ProfileEPUB}, From 3256ba6400d7a731335bcb492a5da79b9d1d24a2 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 21 Mar 2024 18:35:07 -0700 Subject: [PATCH 2/7] only add archive property to link if it doesn't yet exist --- pkg/fetcher/fetcher_archive.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/fetcher/fetcher_archive.go b/pkg/fetcher/fetcher_archive.go index eb1efea0..11e21a0f 100644 --- a/pkg/fetcher/fetcher_archive.go +++ b/pkg/fetcher/fetcher_archive.go @@ -109,12 +109,14 @@ func (r *entryResource) Link() manifest.Link { if cl == 0 { cl = r.entry.Length() } - r.link.Properties.Add(map[string]interface{}{ - "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ - "entryLength": cl, - "isEntryCompressed": r.entry.CompressedLength() > 0, - }, - }) + if r.link.Properties.Get("https://readium.org/webpub-manifest/properties#archive") == nil { + r.link.Properties.Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ + "entryLength": cl, + "isEntryCompressed": r.entry.CompressedLength() > 0, + }, + }) + } return r.link } From 2a971eff87250fcf6decfeaa5c21f8445d308485 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 May 2024 02:54:55 -0700 Subject: [PATCH 3/7] Add Properties() getter to Resource interface --- pkg/fetcher/fetcher_archive.go | 50 ++++++++++++++--------------- pkg/fetcher/fetcher_archive_test.go | 32 +++++++++++++----- pkg/fetcher/fetcher_file.go | 4 +++ pkg/fetcher/resource.go | 12 +++++++ pkg/fetcher/resource_bytes.go | 5 +++ 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/pkg/fetcher/fetcher_archive.go b/pkg/fetcher/fetcher_archive.go index 11e21a0f..e825562b 100644 --- a/pkg/fetcher/fetcher_archive.go +++ b/pkg/fetcher/fetcher_archive.go @@ -36,16 +36,6 @@ func (f *ArchiveFetcher) Links() (manifest.LinkList, error) { link.Type = mt.String() } } - cl := af.CompressedLength() - if cl == 0 { - cl = af.Length() - } - link.Properties.Add(map[string]interface{}{ - "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ - "entryLength": cl, - "isEntryCompressed": af.CompressedLength() > 0, - }, - }) links = append(links, link) } return links, nil @@ -57,10 +47,24 @@ func (f *ArchiveFetcher) Get(link manifest.Link) Resource { if err != nil { return NewFailureResource(link, NotFound(err)) } - return &entryResource{ + er := &entryResource{ link: link, entry: entry, } + + // Compute archive properties + cl := entry.CompressedLength() + if cl == 0 { + cl = entry.Length() + } + er.properties.Add(map[string]interface{}{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ + "entryLength": cl, + "isEntryCompressed": entry.CompressedLength() > 0, + }, + }) + + return er } // Close implements Fetcher @@ -90,8 +94,9 @@ func NewArchiveFetcherFromPathWithFactory(path string, factory archive.ArchiveFa // Resource from archive entry type entryResource struct { - link manifest.Link - entry archive.Entry + link manifest.Link + entry archive.Entry + properties manifest.Properties } // File implements Resource @@ -104,23 +109,16 @@ func (r *entryResource) Close() { // Nothing needs to be done at the moment } +// Link implements Resource func (r *entryResource) Link() manifest.Link { - cl := r.entry.CompressedLength() - if cl == 0 { - cl = r.entry.Length() - } - if r.link.Properties.Get("https://readium.org/webpub-manifest/properties#archive") == nil { - r.link.Properties.Add(map[string]interface{}{ - "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ - "entryLength": cl, - "isEntryCompressed": r.entry.CompressedLength() > 0, - }, - }) - } - return r.link } +// Properties implements Resource +func (r *entryResource) Properties() manifest.Properties { + return r.properties +} + // Read implements Resource func (r *entryResource) Read(start int64, end int64) ([]byte, *ResourceError) { data, err := r.entry.Read(start, end) diff --git a/pkg/fetcher/fetcher_archive_test.go b/pkg/fetcher/fetcher_archive_test.go index 9c7a3c8a..e4e5c04d 100644 --- a/pkg/fetcher/fetcher_archive_test.go +++ b/pkg/fetcher/fetcher_archive_test.go @@ -15,21 +15,31 @@ func withArchiveFetcher(t *testing.T, callback func(a *ArchiveFetcher)) { } func TestArchiveFetcherLinks(t *testing.T) { - makeTestLink := func(href string, typ string, entryLength uint64, isCompressed bool) manifest.Link { + makeTestLink := func(href string, typ string, entryLength uint64, isCompressed bool) struct { + manifest.Link + manifest.Properties + } { l := manifest.Link{ Href: href, Type: typ, } - l.Properties.Add(map[string]interface{}{ + var p manifest.Properties + p.Add(map[string]interface{}{ "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": entryLength, "isEntryCompressed": isCompressed, }, }) - return l + return struct { + manifest.Link + manifest.Properties + }{l, p} } - mustContain := manifest.LinkList{ + mustContain := []struct { + manifest.Link + manifest.Properties + }{ makeTestLink("/mimetype", "", 20, false), makeTestLink("/EPUB/cover.xhtml", "application/xhtml+xml", 259, true), makeTestLink("/EPUB/css/epub.css", "text/css", 595, true), @@ -46,7 +56,12 @@ func TestArchiveFetcherLinks(t *testing.T) { links, err := a.Links() assert.Nil(t, err) - assert.ElementsMatch(t, mustContain, links) + mustLinks := make([]manifest.Link, len(mustContain)) + for i, l := range mustContain { + assert.Equal(t, l.Properties, a.Get(l.Link).Properties()) + mustLinks[i] = l.Link + } + assert.ElementsMatch(t, mustLinks, links) }) } @@ -133,11 +148,11 @@ func TestArchiveFetcherAddsProperties(t *testing.T) { "entryLength": uint64(595), "isEntryCompressed": true, }, - }), resource.Link().Properties) + }), resource.Properties()) }) } -func TestArchiveFetcherOriginalPropertiesKept(t *testing.T) { +/*func TestArchiveFetcherOriginalPropertiesKept(t *testing.T) { withArchiveFetcher(t, func(a *ArchiveFetcher) { l := manifest.Link{Href: "/EPUB/css/epub.css"} l.Properties.Add(map[string]interface{}{"other": "property"}) @@ -148,6 +163,7 @@ func TestArchiveFetcherOriginalPropertiesKept(t *testing.T) { "entryLength": uint64(595), "isEntryCompressed": true, }, - }), resource.Link().Properties) + }), resource.Properties()) }) } +*/ diff --git a/pkg/fetcher/fetcher_file.go b/pkg/fetcher/fetcher_file.go index 431052a8..6c57339c 100644 --- a/pkg/fetcher/fetcher_file.go +++ b/pkg/fetcher/fetcher_file.go @@ -128,6 +128,10 @@ func (r *FileResource) Link() manifest.Link { return r.link } +func (r *FileResource) Properties() manifest.Properties { + return manifest.Properties{} +} + // Close implements Resource func (r *FileResource) Close() { if r.file != nil { diff --git a/pkg/fetcher/resource.go b/pkg/fetcher/resource.go index f4a5d540..b300f804 100644 --- a/pkg/fetcher/resource.go +++ b/pkg/fetcher/resource.go @@ -38,6 +38,10 @@ type Resource interface { // It might be modified by the [Resource] to include additional metadata, e.g. the `Content-Type` HTTP header in [Link.Type]. Link() manifest.Link + // Returns the properties associated with the resource. + // This is opened for extensions. + Properties() manifest.Properties + // Returns data length from metadata if available, or calculated from reading the bytes otherwise. // This value must be treated as a hint, as it might not reflect the actual bytes length. To get the real length, you need to read the whole resource. Length() (int64, *ResourceError) @@ -265,6 +269,10 @@ func (r FailureResource) Link() manifest.Link { return r.link } +func (r FailureResource) Properties() manifest.Properties { + return manifest.Properties{} +} + // Length implements Resource func (r FailureResource) Length() (int64, *ResourceError) { return 0, r.ex @@ -323,6 +331,10 @@ func (r ProxyResource) Link() manifest.Link { return r.Res.Link() } +func (r ProxyResource) Properties() manifest.Properties { + return manifest.Properties{} +} + // Length implements Resource func (r ProxyResource) Length() (int64, *ResourceError) { return r.Res.Length() diff --git a/pkg/fetcher/resource_bytes.go b/pkg/fetcher/resource_bytes.go index 9b813d8e..ddbfd59b 100644 --- a/pkg/fetcher/resource_bytes.go +++ b/pkg/fetcher/resource_bytes.go @@ -29,6 +29,11 @@ func (r *BytesResource) Link() manifest.Link { return r.link } +// Properties implements Resource +func (r *BytesResource) Properties() manifest.Properties { + return manifest.Properties{} +} + // Length implements Resource func (r *BytesResource) Length() (int64, *ResourceError) { bin, err := r.Read(0, 0) From 3595814e75c79275be214817a077ca7d5588cd8c Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 May 2024 16:40:01 -0700 Subject: [PATCH 4/7] revert to simple map[string]interface{} Properties type --- pkg/fetcher/fetcher_archive_test.go | 5 +- pkg/manifest/link.go | 2 +- pkg/manifest/link_test.go | 8 +-- pkg/manifest/properties.go | 89 +++++--------------------- pkg/manifest/properties_test.go | 99 ++++++----------------------- pkg/parser/epub/factory.go | 2 +- 6 files changed, 45 insertions(+), 160 deletions(-) diff --git a/pkg/fetcher/fetcher_archive_test.go b/pkg/fetcher/fetcher_archive_test.go index e4e5c04d..fbb4753d 100644 --- a/pkg/fetcher/fetcher_archive_test.go +++ b/pkg/fetcher/fetcher_archive_test.go @@ -23,13 +23,12 @@ func TestArchiveFetcherLinks(t *testing.T) { Href: href, Type: typ, } - var p manifest.Properties - p.Add(map[string]interface{}{ + p := manifest.Properties{ "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": entryLength, "isEntryCompressed": isCompressed, }, - }) + } return struct { manifest.Link manifest.Properties diff --git a/pkg/manifest/link.go b/pkg/manifest/link.go index 76d9a470..5f10d047 100644 --- a/pkg/manifest/link.go +++ b/pkg/manifest/link.go @@ -196,7 +196,7 @@ func (l Link) MarshalJSON() ([]byte, error) { if len(l.Rels) > 0 { res["rel"] = l.Rels } - if l.Properties.Length() > 0 { + if len(l.Properties) > 0 { res["properties"] = l.Properties } if l.Height > 0 { diff --git a/pkg/manifest/link_test.go b/pkg/manifest/link_test.go index 78e3432a..f34a84b6 100644 --- a/pkg/manifest/link_test.go +++ b/pkg/manifest/link_test.go @@ -71,9 +71,9 @@ func TestLinkUnmarshalFullJSON(t *testing.T) { Templated: true, Title: "Link Title", Rels: []string{"publication", "cover"}, - Properties: (&Properties{}).Add(map[string]interface{}{ + Properties: Properties{ "orientation": "landscape", - }), + }, Height: 1024, Width: 768, Bitrate: 74.2, @@ -188,9 +188,9 @@ func TestLinkFullJSON(t *testing.T) { Templated: true, Title: "Link Title", Rels: []string{"publication", "cover"}, - Properties: (&Properties{}).Add(map[string]interface{}{ + Properties: Properties{ "orientation": "landscape", - }), + }, Height: 1024, Width: 768, Bitrate: 74.2, diff --git a/pkg/manifest/properties.go b/pkg/manifest/properties.go index d8ccd71b..b5c6409b 100644 --- a/pkg/manifest/properties.go +++ b/pkg/manifest/properties.go @@ -2,29 +2,18 @@ package manifest import ( "encoding/json" - "sync" "github.com/pkg/errors" ) -type Properties struct { - properties map[string]interface{} - mutext *sync.RWMutex -} +type Properties map[string]interface{} func (p *Properties) Add(newProperties map[string]interface{}) Properties { - if p == nil { - p = &Properties{} + if *p == nil { + *p = make(Properties) } - if p.properties == nil || p.mutext == nil { - p.properties = make(map[string]interface{}) - p.mutext = &sync.RWMutex{} - } - p.mutext.Lock() - defer p.mutext.Unlock() - for k, v := range newProperties { - p.properties[k] = v + (*p)[k] = v } return *p } @@ -33,45 +22,22 @@ func (p *Properties) Delete(key string) Properties { if p == nil { p = &Properties{} } - if p.properties == nil || p.mutext == nil { - p.properties = make(map[string]interface{}) - p.mutext = &sync.RWMutex{} - } - p.mutext.Lock() - defer p.mutext.Unlock() - - delete(p.properties, key) + delete(*p, key) return *p } -func (p Properties) Get(key string) interface{} { - if p.properties != nil && p.mutext != nil { - p.mutext.RLock() - defer p.mutext.RUnlock() - - return p.properties[key] +func (p *Properties) Get(key string) interface{} { + if p != nil { + return (*p)[key] } return nil } -func (p Properties) Length() int { - if p.properties == nil || p.mutext == nil { - return 0 - } - p.mutext.RLock() - defer p.mutext.RUnlock() - - return len(p.properties) -} - func (p Properties) GetString(key string) string { - if p.properties == nil || p.mutext == nil { + if p == nil { return "" } - p.mutext.RLock() - defer p.mutext.RUnlock() - - v, ok := p.properties[key] + v, ok := p[key] if !ok { return "" } @@ -83,13 +49,10 @@ func (p Properties) GetString(key string) string { } func (p Properties) GetBool(key string) *bool { - if p.properties == nil || p.mutext == nil { + if p == nil { return nil } - p.mutext.RLock() - defer p.mutext.RUnlock() - - v, ok := p.properties[key] + v, ok := p[key] if !ok { return nil } @@ -149,13 +112,10 @@ func (p Properties) Encryption() *Encryption { } func (p Properties) Contains() []string { - if p.properties == nil { + if p == nil { return nil } - p.mutext.RLock() - defer p.mutext.RUnlock() - - v, ok := p.properties["contains"] + v, ok := p["contains"] if !ok { return nil } @@ -168,20 +128,14 @@ func (p Properties) Contains() []string { func PropertiesFromJSON(rawJson interface{}) (Properties, error) { if rawJson == nil { - return Properties{}, nil + return make(Properties), nil } properties, ok := rawJson.(map[string]interface{}) if !ok { - return Properties{}, errors.New("Properties has invalid JSON object") - } - if len(properties) > 0 { - return Properties{ - properties: properties, - mutext: &sync.RWMutex{}, - }, nil + return nil, errors.New("Properties has invalid JSON object") } - return Properties{}, nil + return properties, nil } func (p *Properties) UnmarshalJSON(data []byte) error { @@ -197,12 +151,3 @@ func (p *Properties) UnmarshalJSON(data []byte) error { *p = pr return nil } - -func (p Properties) MarshalJSON() ([]byte, error) { - if p.properties == nil || p.mutext == nil { - return json.Marshal(nil) - } - p.mutext.RLock() - defer p.mutext.RUnlock() - return json.Marshal(p.properties) -} diff --git a/pkg/manifest/properties_test.go b/pkg/manifest/properties_test.go index 0b1a2d53..ebb920a1 100644 --- a/pkg/manifest/properties_test.go +++ b/pkg/manifest/properties_test.go @@ -2,7 +2,6 @@ package manifest import ( "encoding/json" - "sync" "testing" "github.com/stretchr/testify/assert" @@ -28,43 +27,21 @@ func TestPropertiesUnmarshalFullJSON(t *testing.T) { }`), &p)) assert.Equal(t, Properties{ - properties: map[string]interface{}{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, - }, - mutext: &sync.RWMutex{}, + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, }, p) } -func TestPropertiesMarshalFullJSON(t *testing.T) { - p := Properties{ - properties: map[string]interface{}{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, - }, - mutext: &sync.RWMutex{}, - } - b, err := json.Marshal(p) - assert.NoError(t, err) - assert.JSONEq(t, `{"other-property1":"value","other-property2":[42]}`, string(b)) -} - func TestPropertiesAddGiven(t *testing.T) { p2 := Properties{ - properties: map[string]interface{}{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, - }, - mutext: &sync.RWMutex{}, + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, } assert.Equal(t, Properties{ - properties: map[string]interface{}{ - "other-property1": "value", - "other-property2": []interface{}{float64(42)}, - "additional": "property", - }, - mutext: &sync.RWMutex{}, - }, p2.Add(map[string]interface{}{ + "other-property1": "value", + "other-property2": []interface{}{float64(42)}, + "additional": "property", + }, p2.Add(Properties{ "additional": "property", })) } @@ -73,96 +50,60 @@ func TestPropertiesAddGiven(t *testing.T) { func TestPropertiesClippedAvailable(t *testing.T) { assert.Equal(t, true, *Properties{ - properties: map[string]interface{}{ - "clipped": true, - }, - mutext: &sync.RWMutex{}, + "clipped": true, }.Clipped(), "Clipped true when set to true") } func TestPropertiesClippedMissing(t *testing.T) { - assert.Nil(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Clipped(), "Clipped nil when missing") + assert.Nil(t, Properties{}.Clipped(), "Clipped nil when missing") } func TestPropertiesFitAvailable(t *testing.T) { assert.Equal(t, FitCover, Properties{ - properties: map[string]interface{}{ - "fit": "cover", - }, - mutext: &sync.RWMutex{}, + "fit": "cover", }.Fit(), "Fit cover when set to cover") } func TestPropertiesFitMissing(t *testing.T) { - assert.Empty(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Clipped(), "Fit empty when missing") + assert.Empty(t, Properties{}.Clipped(), "Fit empty when missing") } func TestPropertiesOrientationAvailable(t *testing.T) { assert.Equal(t, OrientationLandscape, Properties{ - properties: map[string]interface{}{ - "orientation": "landscape", - }, - mutext: &sync.RWMutex{}, + "orientation": "landscape", }.Orientation(), "Orientation landscape when set to landscape") } func TestPropertiesOrientationMissing(t *testing.T) { - assert.Empty(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Orientation(), "Orientation empty when missing") + assert.Empty(t, Properties{}.Orientation(), "Orientation empty when missing") } func TestPropertiesOverflowAvailable(t *testing.T) { assert.Equal(t, OverflowScrolled, Properties{ - properties: map[string]interface{}{ - "overflow": "scrolled", - }, - mutext: &sync.RWMutex{}, + "overflow": "scrolled", }.Overflow(), "Overflow scrolled when set to scrolled") } func TestPropertiesOverflowMissing(t *testing.T) { - assert.Empty(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Overflow(), "Overflow empty when missing") + assert.Empty(t, Properties{}.Overflow(), "Overflow empty when missing") } func TestPropertiesPageAvailable(t *testing.T) { assert.Equal(t, PageRight, Properties{ - properties: map[string]interface{}{ - "page": "right", - }, - mutext: &sync.RWMutex{}, + "page": "right", }.Page(), "Page right when set to right") } func TestPropertiesPageMissing(t *testing.T) { - assert.Empty(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Page(), "Page empty when missing") + assert.Empty(t, Properties{}.Page(), "Page empty when missing") } func TestPropertiesSpreadAvailable(t *testing.T) { assert.Equal(t, SpreadBoth, Properties{ - properties: map[string]interface{}{ - "spread": "both", - }, - mutext: &sync.RWMutex{}, + "spread": "both", }.Spread(), "Spread both when set to both") } func TestPropertiesSpreadMissing(t *testing.T) { - assert.Empty(t, Properties{ - properties: map[string]interface{}{}, - mutext: &sync.RWMutex{}, - }.Spread(), "Spread empty when missing") + assert.Empty(t, Properties{}.Spread(), "Spread empty when missing") } diff --git a/pkg/parser/epub/factory.go b/pkg/parser/epub/factory.go index 47a78eab..4c3d511f 100644 --- a/pkg/parser/epub/factory.go +++ b/pkg/parser/epub/factory.go @@ -190,7 +190,7 @@ func (f PublicationFactory) computeLink(item Item, fallbackChain []string) manif Alternates: f.computeAlternates(item, fallbackChain), } - if properties.Length() > 0 { + if len(properties) > 0 { ret.Properties = properties } From 7c6b88ae52d5b8b8e6298d9a2c6011657ef8c392 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 May 2024 16:45:48 -0700 Subject: [PATCH 5/7] revert/make more similar --- pkg/manifest/link.go | 2 +- pkg/manifest/properties.go | 2 +- pkg/parser/epub/deobfuscator_test.go | 2 +- pkg/parser/epub/factory.go | 2 +- pkg/parser/epub/positions_service.go | 2 +- pkg/streamer/a11y_infer_test.go | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/manifest/link.go b/pkg/manifest/link.go index 5f10d047..befd3e23 100644 --- a/pkg/manifest/link.go +++ b/pkg/manifest/link.go @@ -107,7 +107,7 @@ func LinkFromJSON(rawJson map[string]interface{}, normalizeHref LinkHrefNormaliz // Properties properties, ok := rawJson["properties"].(map[string]interface{}) if ok { - link.Properties.Add(properties) + link.Properties = properties } // Rels diff --git a/pkg/manifest/properties.go b/pkg/manifest/properties.go index b5c6409b..11d2d18c 100644 --- a/pkg/manifest/properties.go +++ b/pkg/manifest/properties.go @@ -8,7 +8,7 @@ import ( type Properties map[string]interface{} -func (p *Properties) Add(newProperties map[string]interface{}) Properties { +func (p *Properties) Add(newProperties Properties) Properties { if *p == nil { *p = make(Properties) } diff --git a/pkg/parser/epub/deobfuscator_test.go b/pkg/parser/epub/deobfuscator_test.go index 41b9ce03..0e88753f 100644 --- a/pkg/parser/epub/deobfuscator_test.go +++ b/pkg/parser/epub/deobfuscator_test.go @@ -28,7 +28,7 @@ func withDeobfuscator(t *testing.T, href string, algorithm string, start, end in Href: href, } if algorithm != "" { - link.Properties.Add(map[string]interface{}{ + link.Properties.Add(manifest.Properties{ "encrypted": map[string]interface{}{ "algorithm": algorithm, }, diff --git a/pkg/parser/epub/factory.go b/pkg/parser/epub/factory.go index 4c3d511f..f8a2c746 100644 --- a/pkg/parser/epub/factory.go +++ b/pkg/parser/epub/factory.go @@ -231,7 +231,7 @@ func (f PublicationFactory) computePropertiesAndRels(item Item, itemref *ItemRef properties["encrypted"] = edat.ToMap() // ToMap makes it JSON-like } - return rels, (&manifest.Properties{}).Add(properties) + return rels, manifest.Properties(properties) } // Compute alternate links for [item], checking for an infinite recursion diff --git a/pkg/parser/epub/positions_service.go b/pkg/parser/epub/positions_service.go index 091105bb..0da84cff 100644 --- a/pkg/parser/epub/positions_service.go +++ b/pkg/parser/epub/positions_service.go @@ -173,7 +173,7 @@ func (l ArchiveEntryLength) PositionCount(resource fetcher.Resource) uint { var length uint64 props := resource.Link().Properties if p := props.Get("https://readium.org/webpub-manifest/properties#archive"); p != nil { - if pm, ok := p.(map[string]interface{}); ok { + if pm, ok := p.(manifest.Properties); ok { if el, ok := pm["entryLength"].(uint64); ok { length = el } diff --git a/pkg/streamer/a11y_infer_test.go b/pkg/streamer/a11y_infer_test.go index b6fc94c1..3123ee10 100644 --- a/pkg/streamer/a11y_infer_test.go +++ b/pkg/streamer/a11y_infer_test.go @@ -212,9 +212,9 @@ func TestInferFeaturePageList(t *testing.T) { // "resources" in RWPM) func TestInferFeatureMathML(t *testing.T) { link := newLink(mediatype.HTML, "html") - link.Properties.Add(map[string]interface{}{ + link.Properties = manifest.Properties{ "contains": []string{"mathml"}, - }) + } m := manifest.Manifest{ Metadata: manifest.Metadata{ ConformsTo: []manifest.Profile{manifest.ProfileEPUB}, From 050555d6071f122913da2d37cb3a415cf19f5caf Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 May 2024 16:50:53 -0700 Subject: [PATCH 6/7] Remove Add/Delete functions from properties --- pkg/fetcher/fetcher_archive_test.go | 20 ++------------------ pkg/manifest/properties.go | 7 +++++-- pkg/manifest/properties_test.go | 4 ++-- pkg/parser/epub/deobfuscator_test.go | 8 +++----- pkg/parser/epub/factory.go | 4 ++-- 5 files changed, 14 insertions(+), 29 deletions(-) diff --git a/pkg/fetcher/fetcher_archive_test.go b/pkg/fetcher/fetcher_archive_test.go index fbb4753d..e3024df5 100644 --- a/pkg/fetcher/fetcher_archive_test.go +++ b/pkg/fetcher/fetcher_archive_test.go @@ -142,27 +142,11 @@ func TestArchiveFetcherFileNotFoundLength(t *testing.T) { func TestArchiveFetcherAddsProperties(t *testing.T) { withArchiveFetcher(t, func(a *ArchiveFetcher) { resource := a.Get(manifest.Link{Href: "/EPUB/css/epub.css"}) - assert.Equal(t, (&manifest.Properties{}).Add(map[string]interface{}{ + assert.Equal(t, manifest.Properties{ "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ "entryLength": uint64(595), "isEntryCompressed": true, }, - }), resource.Properties()) + }, resource.Properties()) }) } - -/*func TestArchiveFetcherOriginalPropertiesKept(t *testing.T) { - withArchiveFetcher(t, func(a *ArchiveFetcher) { - l := manifest.Link{Href: "/EPUB/css/epub.css"} - l.Properties.Add(map[string]interface{}{"other": "property"}) - resource := a.Get(l) - assert.Equal(t, (&manifest.Properties{}).Add(map[string]interface{}{ - "other": "property", - "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ - "entryLength": uint64(595), - "isEntryCompressed": true, - }, - }), resource.Properties()) - }) -} -*/ diff --git a/pkg/manifest/properties.go b/pkg/manifest/properties.go index 11d2d18c..3eee516b 100644 --- a/pkg/manifest/properties.go +++ b/pkg/manifest/properties.go @@ -8,7 +8,10 @@ import ( type Properties map[string]interface{} -func (p *Properties) Add(newProperties Properties) Properties { +// Properties should be immutable, therefore these functions have been removed. +// The code is left here in case it's useful in a future implementation. + +/*func (p *Properties) Add(newProperties Properties) Properties { if *p == nil { *p = make(Properties) } @@ -24,7 +27,7 @@ func (p *Properties) Delete(key string) Properties { } delete(*p, key) return *p -} +}*/ func (p *Properties) Get(key string) interface{} { if p != nil { diff --git a/pkg/manifest/properties_test.go b/pkg/manifest/properties_test.go index ebb920a1..e5bb5998 100644 --- a/pkg/manifest/properties_test.go +++ b/pkg/manifest/properties_test.go @@ -32,7 +32,7 @@ func TestPropertiesUnmarshalFullJSON(t *testing.T) { }, p) } -func TestPropertiesAddGiven(t *testing.T) { +/*func TestPropertiesAddGiven(t *testing.T) { p2 := Properties{ "other-property1": "value", "other-property2": []interface{}{float64(42)}, @@ -44,7 +44,7 @@ func TestPropertiesAddGiven(t *testing.T) { }, p2.Add(Properties{ "additional": "property", })) -} +}*/ // Presentation-specific properties diff --git a/pkg/parser/epub/deobfuscator_test.go b/pkg/parser/epub/deobfuscator_test.go index 0e88753f..599acaa0 100644 --- a/pkg/parser/epub/deobfuscator_test.go +++ b/pkg/parser/epub/deobfuscator_test.go @@ -28,11 +28,9 @@ func withDeobfuscator(t *testing.T, href string, algorithm string, start, end in Href: href, } if algorithm != "" { - link.Properties.Add(manifest.Properties{ - "encrypted": map[string]interface{}{ - "algorithm": algorithm, - }, - }) + link.Properties["encrypted"] = map[string]interface{}{ + "algorithm": algorithm, + } } obfu, err := NewDeobfuscator(identifier).Transform(ft.Get(link)).Read(start, end) if !assert.Nil(t, err) { diff --git a/pkg/parser/epub/factory.go b/pkg/parser/epub/factory.go index f8a2c746..c6d13b27 100644 --- a/pkg/parser/epub/factory.go +++ b/pkg/parser/epub/factory.go @@ -136,9 +136,9 @@ func mapEPUBLink(link EPUBLink) manifest.Link { } if len(contains) > 0 { - l.Properties.Add(map[string]interface{}{ + l.Properties = manifest.Properties{ "contains": contains, - }) + } } return l From 849b8eb0264dd3bc8f91d7a3079a043b70575d9a Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 May 2024 16:55:29 -0700 Subject: [PATCH 7/7] fixes --- pkg/fetcher/fetcher_archive.go | 19 ++++++++++--------- pkg/parser/epub/deobfuscator_test.go | 6 ++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pkg/fetcher/fetcher_archive.go b/pkg/fetcher/fetcher_archive.go index e825562b..7df687b9 100644 --- a/pkg/fetcher/fetcher_archive.go +++ b/pkg/fetcher/fetcher_archive.go @@ -47,22 +47,23 @@ func (f *ArchiveFetcher) Get(link manifest.Link) Resource { if err != nil { return NewFailureResource(link, NotFound(err)) } - er := &entryResource{ - link: link, - entry: entry, - } // Compute archive properties cl := entry.CompressedLength() if cl == 0 { cl = entry.Length() } - er.properties.Add(map[string]interface{}{ - "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ - "entryLength": cl, - "isEntryCompressed": entry.CompressedLength() > 0, + + er := &entryResource{ + link: link, + entry: entry, + properties: manifest.Properties{ + "https://readium.org/webpub-manifest/properties#archive": map[string]interface{}{ + "entryLength": cl, + "isEntryCompressed": entry.CompressedLength() > 0, + }, }, - }) + } return er } diff --git a/pkg/parser/epub/deobfuscator_test.go b/pkg/parser/epub/deobfuscator_test.go index 599acaa0..ba30d42f 100644 --- a/pkg/parser/epub/deobfuscator_test.go +++ b/pkg/parser/epub/deobfuscator_test.go @@ -28,8 +28,10 @@ func withDeobfuscator(t *testing.T, href string, algorithm string, start, end in Href: href, } if algorithm != "" { - link.Properties["encrypted"] = map[string]interface{}{ - "algorithm": algorithm, + link.Properties = manifest.Properties{ + "encrypted": map[string]interface{}{ + "algorithm": algorithm, + }, } } obfu, err := NewDeobfuscator(identifier).Transform(ft.Get(link)).Read(start, end)