@@ -33,12 +33,14 @@ type ContentfulCache struct {
33
33
genericEntries map[string]*GenericEntry
34
34
idContentTypeMap map[string]string
35
35
parentMap map[string][]EntryReference
36
+ tags tagsCacheMap
36
37
}
37
38
38
39
type ContentfulCacheMutex struct {
39
40
fullCacheGcLock sync.RWMutex
40
41
sharedDataGcLock sync.RWMutex
41
42
assetsGcLock sync.RWMutex
43
+ tagGcLock sync.RWMutex
42
44
idContentTypeMapGcLock sync.RWMutex
43
45
parentMapGcLock sync.RWMutex
44
46
genericEntriesGcLock sync.RWMutex
@@ -48,6 +50,8 @@ type ContentfulCacheMutex struct {
48
50
49
51
type assetCacheMap map[string]*contentful.Asset
50
52
53
+ type tagsCacheMap map[string]string
54
+
51
55
type ContentfulClient struct {
52
56
Cache *ContentfulCache
53
57
cacheInit bool
@@ -77,6 +81,7 @@ type ContentfulClient struct {
77
81
type offlineTemp struct {
78
82
Entries []contentful.Entry `json:"entries"`
79
83
Assets []contentful.Asset `json:"assets"`
84
+ Tags []contentful.Tag `json:"tags"`
80
85
}
81
86
82
87
type ContentTypeResult struct {
@@ -139,6 +144,7 @@ var SpaceLocales = []Locale{ {{ range $index , $locale := $locales }}
139
144
const (
140
145
assetPageSize = 1000
141
146
assetWorkerType = "_asset"
147
+ tagWorkerType = "_tag"
142
148
)
143
149
144
150
const cacheUpdateConcurrency = 4
@@ -160,6 +166,7 @@ var (
160
166
InfoUpdatedEntityCache = "updated cache for entity"
161
167
InfoCachedAllEntries = "cached all entries of content type"
162
168
InfoCachedAllAssets = "cached all assets"
169
+ InfoCachedAllTags = "cached all tags"
163
170
InfoFallingBackToFile = "gonna use a local file"
164
171
InfoLoadingFromFile = "loading space from local file"
165
172
InfoCacheIsNil = "contentful cache is nil"
@@ -299,6 +306,39 @@ func (cc *ContentfulClient) GetAllAssets(ctx context.Context) (map[string]*conte
299
306
return cc.getAllAssets(ctx, true)
300
307
}
301
308
309
+ func (cc *ContentfulClient) GetAssetsByTag(ctx context.Context, tagName string) (vos []*contentful.Asset, err error) {
310
+ if cc == nil || cc.Client == nil {
311
+ return nil, errors.New("GetAssetsByTag: No client available")
312
+ }
313
+ if !cc.cacheInit {
314
+ return nil, errors.New("GetAssetsByTag: only available with cache")
315
+ }
316
+ tags, err := cc.getAllTags(ctx, true)
317
+ if err != nil {
318
+ return nil, errors.New("GetAssetsByTag could not get tags from cache: " + err.Error())
319
+ }
320
+ cc.cacheMutex.assetsGcLock.RLock()
321
+ defer cc.cacheMutex.assetsGcLock.RUnlock()
322
+ if _, tagExists := tags[tagName]; !tagExists {
323
+ return nil, nil
324
+ }
325
+ tagID := tags[tagName]
326
+ for _, vo := range cc.Cache.assets {
327
+ for _, voTag := range vo.Metadata.Tags {
328
+ if voTag.Sys.ID == tagID {
329
+ vos = append(vos, vo)
330
+ }
331
+ }
332
+ }
333
+ return vos, nil
334
+ }
335
+
336
+
337
+
338
+ func (cc *ContentfulClient) GetAllTags(ctx context.Context) (map[string]string, error) {
339
+ return cc.getAllTags(ctx, true)
340
+ }
341
+
302
342
func (cc *ContentfulClient) GetAssetByID(ctx context.Context, id string, forceNoCache ...bool) (*contentful.Asset, error) {
303
343
if cc == nil || cc.Client == nil {
304
344
return nil, errors.New("GetAssetByID: No client available")
@@ -1228,6 +1268,16 @@ func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []stri
1228
1268
}
1229
1269
}
1230
1270
}
1271
+ tags, err := cc.GetAllTags(ctx)
1272
+ if err != nil {
1273
+ if cc.logFn != nil && cc.logLevel <= LogWarn {
1274
+ cc.logFn(map[string]interface{}{"task": "UpdateCache", "error": err.Error()}, LogWarn, "failed to cache tags")
1275
+ }
1276
+ }
1277
+ cc.cacheMutex.tagGcLock.Lock()
1278
+ cc.Cache.tags = tags
1279
+ cc.cacheMutex.tagGcLock.Unlock()
1280
+
1231
1281
if isSync {
1232
1282
return cc.syncCache(ctxAtWork, contentTypes)
1233
1283
}
@@ -1418,7 +1468,7 @@ func (cc *ContentfulClient) cacheSpace(ctx context.Context, contentTypes []strin
1418
1468
parentMap: map[string][]EntryReference{},
1419
1469
}
1420
1470
if cacheAssets {
1421
- contentTypes = append([]string{assetWorkerType}, contentTypes...)
1471
+ contentTypes = append([]string{assetWorkerType, tagWorkerType }, contentTypes...)
1422
1472
}
1423
1473
_, errCanWeEvenConnect := cc.Client.Spaces.Get(ctx, cc.SpaceID)
1424
1474
cc.cacheMutex.sharedDataGcLock.RLock()
@@ -1669,6 +1719,49 @@ func (cc *ContentfulClient) getAllAssets(ctx context.Context, tryCacheFirst bool
1669
1719
return assets, nil
1670
1720
}
1671
1721
1722
+ func (cc *ContentfulClient) getAllTags(ctx context.Context, tryCacheFirst bool) (map[string]string, error) {
1723
+ if cc == nil || cc.Client == nil {
1724
+ return nil, errors.New("getAllTags: No client available")
1725
+ }
1726
+ cc.cacheMutex.sharedDataGcLock.RLock()
1727
+ offline := cc.offline
1728
+ cacheInit := cc.cacheInit
1729
+ cc.cacheMutex.sharedDataGcLock.RUnlock()
1730
+ cc.cacheMutex.tagGcLock.RLock()
1731
+ defer cc.cacheMutex.tagGcLock.RUnlock()
1732
+ if cacheInit && cc.Cache.tags != nil && tryCacheFirst {
1733
+ return cc.Cache.tags, nil
1734
+ }
1735
+ allItems := []interface{}{}
1736
+ tags := map[string]string{}
1737
+ if offline {
1738
+ for _, asset := range cc.offlineTemp.Tags {
1739
+ allItems = append(allItems, asset)
1740
+ }
1741
+ } else {
1742
+ col := cc.Client.Tags.List(ctx, cc.SpaceID)
1743
+ col.Query.Limit(1000)
1744
+ _, err := col.Next()
1745
+ if err != nil {
1746
+ return nil, err
1747
+ }
1748
+ allItems = col.Items
1749
+ }
1750
+ for _, item := range allItems {
1751
+ tag := contentful.Tag{}
1752
+ byt, err := json.Marshal(item)
1753
+ if err != nil {
1754
+ return nil, err
1755
+ }
1756
+ err = json.Unmarshal(byt, &tag)
1757
+ if err != nil {
1758
+ return nil, err
1759
+ }
1760
+ tags[tag.Name] = tag.Sys.ID
1761
+ }
1762
+ return tags, nil
1763
+ }
1764
+
1672
1765
func getOfflineSpaceFromFile(file []byte) (*offlineTemp, error) {
1673
1766
offlineTemp := &offlineTemp{}
1674
1767
err := json.Unmarshal(file, offlineTemp)
@@ -2149,6 +2242,16 @@ func updateCacheForContentType(ctx context.Context, results chan ContentTypeResu
2149
2242
if cc.logFn != nil && cc.logLevel <= LogInfo {
2150
2243
cc.logFn(map[string]interface{}{"contentType": "asset", "method": "updateCacheForContentType", "size": len(allAssets)}, LogInfo, InfoCachedAllAssets)
2151
2244
}
2245
+
2246
+ case tagWorkerType:
2247
+ allTags, err := cc.getAllTags(ctx, false)
2248
+ if err != nil {
2249
+ return errors.New("updateCacheForContentType failed for tags")
2250
+ }
2251
+ tempCache.tags = allTags
2252
+ if cc.logFn != nil && cc.logLevel <= LogInfo {
2253
+ cc.logFn(map[string]interface{}{"contentType": "tag", "method": "updateCacheForContentType", "size": len(allTags)}, LogInfo, InfoCachedAllTags)
2254
+ }
2152
2255
}
2153
2256
return nil
2154
2257
}
0 commit comments