diff --git a/common/maps/maps.go b/common/maps/maps.go index 5552da55d..2d8a122ca 100644 --- a/common/maps/maps.go +++ b/common/maps/maps.go @@ -109,6 +109,20 @@ func ToSliceStringMap(in any) ([]map[string]any, error) { } } +// LookupEqualFold finds key in m with case insensitive equality checks. +func LookupEqualFold[T any | string](m map[string]T, key string) (T, bool) { + if v, found := m[key]; found { + return v, true + } + for k, v := range m { + if strings.EqualFold(k, key) { + return v, true + } + } + var s T + return s, false +} + type keyRename struct { pattern glob.Glob newKey string diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index 53120dce7..0b84d2dd7 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -171,3 +171,26 @@ func TestRenameKeys(t *testing.T) { t.Errorf("Expected\n%#v, got\n%#v\n", expected, m) } } + +func TestLookupEqualFold(t *testing.T) { + c := qt.New(t) + + m1 := map[string]any{ + "a": "av", + "B": "bv", + } + + v, found := LookupEqualFold(m1, "b") + c.Assert(found, qt.IsTrue) + c.Assert(v, qt.Equals, "bv") + + m2 := map[string]string{ + "a": "av", + "B": "bv", + } + + v, found = LookupEqualFold(m2, "b") + c.Assert(found, qt.IsTrue) + c.Assert(v, qt.Equals, "bv") + +} diff --git a/docs/content/en/hugo-pipes/introduction.md b/docs/content/en/hugo-pipes/introduction.md index 83d64d1d3..bbafe55b2 100755 --- a/docs/content/en/hugo-pipes/introduction.md +++ b/docs/content/en/hugo-pipes/introduction.md @@ -53,6 +53,19 @@ With `resources.GetRemote`, the first argument is a remote URL: `resources.Get` and `resources.GetRemote` return `nil` if the resource is not found. +### Caching + +By default, Hugo calculates a cache key based on the `URL` and the `options` (e.g. headers) given. + + +{{< new-in "0.97.0" >}} You can override this by setting a `key` in the options map. This can be used to get more fine grained control over how often a remote resource is fetched, e.g.: + + +```go-html-template +{{ $cacheKey := print $url (now.Format "2006-01-02") }} +{{ $resource := resource.GetRemote $url (dict "key" $cacheKey) }} +``` + ### Error Handling {{< new-in "0.91.0" >}} diff --git a/resources/resource_factories/create/remote.go b/resources/resource_factories/create/remote.go index 56bd99cb1..32dfafe5c 100644 --- a/resources/resource_factories/create/remote.go +++ b/resources/resource_factories/create/remote.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/media" @@ -79,7 +80,7 @@ func (c *Client) FromRemote(uri string, optionsm map[string]any) (resource.Resou return nil, errors.Wrapf(err, "failed to parse URL for resource %s", uri) } - resourceID := helpers.HashString(uri, optionsm) + resourceID := calculateResourceID(uri, optionsm) _, httpResponse, err := c.cacheGetResource.GetOrCreate(resourceID, func() (io.ReadCloser, error) { options, err := decodeRemoteOptions(optionsm) @@ -199,6 +200,13 @@ func (c *Client) validateFromRemoteArgs(uri string, options fromRemoteOptions) e return nil } +func calculateResourceID(uri string, optionsm map[string]any) string { + if key, found := maps.LookupEqualFold(optionsm, "key"); found { + return helpers.HashString(key) + } + return helpers.HashString(uri, optionsm) +} + func addDefaultHeaders(req *http.Request, accepts ...string) { for _, accept := range accepts { if !hasHeaderValue(req.Header, "Accept", accept) { diff --git a/resources/resource_factories/create/remote_test.go b/resources/resource_factories/create/remote_test.go index cfd18269f..c2a3b7b32 100644 --- a/resources/resource_factories/create/remote_test.go +++ b/resources/resource_factories/create/remote_test.go @@ -83,3 +83,14 @@ func TestDecodeRemoteOptions(t *testing.T) { } } + +func TestCalculateResourceID(t *testing.T) { + c := qt.New(t) + + c.Assert(calculateResourceID("foo", nil), qt.Equals, "5917621528921068675") + c.Assert(calculateResourceID("foo", map[string]any{"bar": "baz"}), qt.Equals, "7294498335241413323") + + c.Assert(calculateResourceID("foo", map[string]any{"key": "1234", "bar": "baz"}), qt.Equals, "14904296279238663669") + c.Assert(calculateResourceID("asdf", map[string]any{"key": "1234", "bar": "asdf"}), qt.Equals, "14904296279238663669") + c.Assert(calculateResourceID("asdf", map[string]any{"key": "12345", "bar": "asdf"}), qt.Equals, "12191037851845371770") +}