summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-04-11 10:34:08 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-04-13 09:18:17 +0200
commit2dbdf38a5411c554146ddd915a0aea3b2eaf4407 (patch)
tree333aaaac81aff606b1e6d39adc1663cec0fa8a32
parentf8c4e1690a462a2dcafa05aeaab65d1fad6df61e (diff)
resources: Add `key` to reources.GetRemote options map
If set, `key` will be used as the only cache key element for the resource. The default behaviour is to calculate the key based on the URL and all the options. This means that you can now do: ``` {{ $cacheKey := print $url (now.Format "2006-01-02") }} {{ $resource := resource.GetRemote $url (dict "key" $cacheKey) }} ``` Fixes #9755
-rw-r--r--common/maps/maps.go14
-rw-r--r--common/maps/maps_test.go23
-rwxr-xr-xdocs/content/en/hugo-pipes/introduction.md13
-rw-r--r--resources/resource_factories/create/remote.go10
-rw-r--r--resources/resource_factories/create/remote_test.go11
5 files changed, 70 insertions, 1 deletions
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")
+}