Skip to content

Commit 975e4a7

Browse files
author
Cristian Vidmar
committed
feat: Tags support, Archived flag
1 parent be3d18b commit 975e4a7

11 files changed

+437
-27
lines changed

erm/templates/contentful_vo.gotmpl

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "github.com/foomo/contentful"
55

66
{{ range $index , $contentType := $contentTypes }}
77
type Cf{{ firstCap $contentType.Sys.ID }} struct {
8+
Metadata *contentful.Metadata `json:"metadata,omitempty"`
89
Sys ContentfulSys `json:"sys,omitempty"`
910
Fields Cf{{ firstCap $contentType.Sys.ID }}Fields `json:"fields,omitempty"`
1011
RawFields RawFields `json:"-"`

erm/templates/contentful_vo_base.gotmpl

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ type ContentfulSys struct {
2424
UpdatedAt string `json:"updatedAt,omitempty"`
2525
Revision float64 `json:"revision,omitempty"`
2626
Version float64 `json:"version,omitempty"`
27+
ArchivedAt string `json:"archivedAt,omitempty"`
28+
ArchivedBy *ContentTypeSysAttributes `json:"archivedBy,omitempty"`
29+
ArchivedVersion int `json:"archivedVersion,omitempty"`
2730
PublishedCounter float64 `json:"publishedCounter,omitempty"`
2831
PublishedVersion float64 `json:"publishedVersion,omitempty"`
2932
}

erm/templates/contentful_vo_lib.gotmpl

+104-1
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ type ContentfulCache struct {
3333
genericEntries map[string]*GenericEntry
3434
idContentTypeMap map[string]string
3535
parentMap map[string][]EntryReference
36+
tags tagsCacheMap
3637
}
3738

3839
type ContentfulCacheMutex struct {
3940
fullCacheGcLock sync.RWMutex
4041
sharedDataGcLock sync.RWMutex
4142
assetsGcLock sync.RWMutex
43+
tagGcLock sync.RWMutex
4244
idContentTypeMapGcLock sync.RWMutex
4345
parentMapGcLock sync.RWMutex
4446
genericEntriesGcLock sync.RWMutex
@@ -48,6 +50,8 @@ type ContentfulCacheMutex struct {
4850

4951
type assetCacheMap map[string]*contentful.Asset
5052

53+
type tagsCacheMap map[string]string
54+
5155
type ContentfulClient struct {
5256
Cache *ContentfulCache
5357
cacheInit bool
@@ -77,6 +81,7 @@ type ContentfulClient struct {
7781
type offlineTemp struct {
7882
Entries []contentful.Entry `json:"entries"`
7983
Assets []contentful.Asset `json:"assets"`
84+
Tags []contentful.Tag `json:"tags"`
8085
}
8186

8287
type ContentTypeResult struct {
@@ -139,6 +144,7 @@ var SpaceLocales = []Locale{ {{ range $index , $locale := $locales }}
139144
const (
140145
assetPageSize = 1000
141146
assetWorkerType = "_asset"
147+
tagWorkerType = "_tag"
142148
)
143149

144150
const cacheUpdateConcurrency = 4
@@ -160,6 +166,7 @@ var (
160166
InfoUpdatedEntityCache = "updated cache for entity"
161167
InfoCachedAllEntries = "cached all entries of content type"
162168
InfoCachedAllAssets = "cached all assets"
169+
InfoCachedAllTags = "cached all tags"
163170
InfoFallingBackToFile = "gonna use a local file"
164171
InfoLoadingFromFile = "loading space from local file"
165172
InfoCacheIsNil = "contentful cache is nil"
@@ -299,6 +306,39 @@ func (cc *ContentfulClient) GetAllAssets(ctx context.Context) (map[string]*conte
299306
return cc.getAllAssets(ctx, true)
300307
}
301308

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+
302342
func (cc *ContentfulClient) GetAssetByID(ctx context.Context, id string, forceNoCache ...bool) (*contentful.Asset, error) {
303343
if cc == nil || cc.Client == nil {
304344
return nil, errors.New("GetAssetByID: No client available")
@@ -1228,6 +1268,16 @@ func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []stri
12281268
}
12291269
}
12301270
}
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+
12311281
if isSync {
12321282
return cc.syncCache(ctxAtWork, contentTypes)
12331283
}
@@ -1418,7 +1468,7 @@ func (cc *ContentfulClient) cacheSpace(ctx context.Context, contentTypes []strin
14181468
parentMap: map[string][]EntryReference{},
14191469
}
14201470
if cacheAssets {
1421-
contentTypes = append([]string{assetWorkerType}, contentTypes...)
1471+
contentTypes = append([]string{assetWorkerType, tagWorkerType}, contentTypes...)
14221472
}
14231473
_, errCanWeEvenConnect := cc.Client.Spaces.Get(ctx, cc.SpaceID)
14241474
cc.cacheMutex.sharedDataGcLock.RLock()
@@ -1669,6 +1719,49 @@ func (cc *ContentfulClient) getAllAssets(ctx context.Context, tryCacheFirst bool
16691719
return assets, nil
16701720
}
16711721

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+
16721765
func getOfflineSpaceFromFile(file []byte) (*offlineTemp, error) {
16731766
offlineTemp := &offlineTemp{}
16741767
err := json.Unmarshal(file, offlineTemp)
@@ -2149,6 +2242,16 @@ func updateCacheForContentType(ctx context.Context, results chan ContentTypeResu
21492242
if cc.logFn != nil && cc.logLevel <= LogInfo {
21502243
cc.logFn(map[string]interface{}{"contentType": "asset", "method": "updateCacheForContentType", "size": len(allAssets)}, LogInfo, InfoCachedAllAssets)
21512244
}
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+
}
21522255
}
21532256
return nil
21542257
}

erm/templates/contentful_vo_lib_contenttype.gotmpl

+49
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,33 @@ func (cc *ContentfulClient) GetFiltered{{ firstCap $contentType.Sys.ID }}(ctx co
6666
return {{ $contentType.Sys.ID }}Map, nil
6767
}
6868

69+
func (cc *ContentfulClient) Get{{ firstCap $contentType.Sys.ID }}ByTag(ctx context.Context, tagName string) (vos []*Cf{{ firstCap $contentType.Sys.ID }}, err error) {
70+
if cc == nil || cc.Client == nil {
71+
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag: No client available")
72+
}
73+
if !cc.cacheInit {
74+
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag: only available with cache")
75+
}
76+
tags, err := cc.getAllTags(ctx, true)
77+
if err != nil {
78+
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag could not get tags from cache: " + err.Error())
79+
}
80+
cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.RLock()
81+
defer cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.RUnlock()
82+
if _, tagExists := tags[tagName]; !tagExists {
83+
return nil, nil
84+
}
85+
tagID := tags[tagName]
86+
for _, vo := range cc.Cache.entryMaps.{{ $contentType.Sys.ID }} {
87+
for _, voTag := range vo.Metadata.Tags {
88+
if voTag.Sys.ID == tagID {
89+
vos = append(vos, vo)
90+
}
91+
}
92+
}
93+
return vos, nil
94+
}
95+
6996
func (cc *ContentfulClient) Get{{ firstCap $contentType.Sys.ID }}ByID(ctx context.Context, id string, forceNoCache ...bool) (vo *Cf{{ firstCap $contentType.Sys.ID }}, err error) {
7097
if cc == nil || cc.Client == nil {
7198
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByID: No client available")
@@ -468,6 +495,28 @@ func (vo *Cf{{ firstCap $contentType.Sys.ID }}) {{ firstCap $field.ID }}(ctx con
468495
{{ end }}
469496
{{ end }}
470497

498+
func (vo *Cf{{ firstCap $contentType.Sys.ID }}) IsArchived(ctx context.Context) (bool, error) {
499+
if vo == nil {
500+
return false, errors.New("IsArchived: Value Object is nil")
501+
}
502+
if vo.CC == nil {
503+
return false, errors.New("IsArchived: Value Object has nil Contentful client")
504+
}
505+
if vo.CC.clientMode != ClientModeCMA {
506+
return false, errors.New("IsArchived: Only available in ClientModeCMA")
507+
}
508+
cfEntry := &contentful.Entry{}
509+
tmp, errMarshal := json.Marshal(vo)
510+
if errMarshal != nil {
511+
return false, errors.New("Cf{{ firstCap $contentType.Sys.ID }} IsArchived: Can't marshal JSON from VO")
512+
}
513+
errUnmarshal := json.Unmarshal(tmp, &cfEntry)
514+
if errUnmarshal != nil {
515+
return false, errors.New("Cf{{ firstCap $contentType.Sys.ID }} IsArchived: Can't unmarshal JSON into CF entry")
516+
}
517+
return len(cfEntry.Sys.ArchivedAt) > 0, nil
518+
}
519+
471520
// {{ firstCap $contentType.Sys.ID }} Field setters
472521
{{ range $fieldIndex, $field := $contentType.Fields }}
473522
func (vo *Cf{{ firstCap $contentType.Sys.ID }}) Set{{ firstCap $field.ID }}({{ $field.ID }} {{ mapFieldType $contentType.Sys.ID $field }}, locale ...Locale) (err error) {

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/foomo/gocontentful
33
go 1.21
44

55
require (
6-
github.com/foomo/contentful v0.5.1
6+
github.com/foomo/contentful v0.5.2
77
github.com/pkg/errors v0.9.1
88
github.com/sirupsen/logrus v1.9.3
99
github.com/stretchr/testify v1.9.0

test/testapi/gocontentfulvo.go

+15-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/testapi/gocontentfulvobase.go

+15-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)