Skip to content

Commit

Permalink
Merge pull request #23 from foomo/feature/contentfultags
Browse files Browse the repository at this point in the history
Tags support, Archived flag
  • Loading branch information
cvidmar authored Oct 31, 2024
2 parents be3d18b + e797ded commit d981374
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 29 deletions.
1 change: 1 addition & 0 deletions erm/templates/contentful_vo.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/foomo/contentful"

{{ range $index , $contentType := $contentTypes }}
type Cf{{ firstCap $contentType.Sys.ID }} struct {
Metadata *contentful.Metadata `json:"metadata,omitempty"`
Sys ContentfulSys `json:"sys,omitempty"`
Fields Cf{{ firstCap $contentType.Sys.ID }}Fields `json:"fields,omitempty"`
RawFields RawFields `json:"-"`
Expand Down
3 changes: 3 additions & 0 deletions erm/templates/contentful_vo_base.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type ContentfulSys struct {
UpdatedAt string `json:"updatedAt,omitempty"`
Revision float64 `json:"revision,omitempty"`
Version float64 `json:"version,omitempty"`
ArchivedAt string `json:"archivedAt,omitempty"`
ArchivedBy *ContentTypeSysAttributes `json:"archivedBy,omitempty"`
ArchivedVersion int `json:"archivedVersion,omitempty"`
PublishedCounter float64 `json:"publishedCounter,omitempty"`
PublishedVersion float64 `json:"publishedVersion,omitempty"`
}
Expand Down
105 changes: 104 additions & 1 deletion erm/templates/contentful_vo_lib.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ type ContentfulCache struct {
genericEntries map[string]*GenericEntry
idContentTypeMap map[string]string
parentMap map[string][]EntryReference
tags tagsCacheMap
}

type ContentfulCacheMutex struct {
fullCacheGcLock sync.RWMutex
sharedDataGcLock sync.RWMutex
assetsGcLock sync.RWMutex
tagGcLock sync.RWMutex
idContentTypeMapGcLock sync.RWMutex
parentMapGcLock sync.RWMutex
genericEntriesGcLock sync.RWMutex
Expand All @@ -48,6 +50,8 @@ type ContentfulCacheMutex struct {

type assetCacheMap map[string]*contentful.Asset

type tagsCacheMap map[string]string

type ContentfulClient struct {
Cache *ContentfulCache
cacheInit bool
Expand Down Expand Up @@ -77,6 +81,7 @@ type ContentfulClient struct {
type offlineTemp struct {
Entries []contentful.Entry `json:"entries"`
Assets []contentful.Asset `json:"assets"`
Tags []contentful.Tag `json:"tags"`
}

type ContentTypeResult struct {
Expand Down Expand Up @@ -139,6 +144,7 @@ var SpaceLocales = []Locale{ {{ range $index , $locale := $locales }}
const (
assetPageSize = 1000
assetWorkerType = "_asset"
tagWorkerType = "_tag"
)

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

func (cc *ContentfulClient) GetAssetsByTag(ctx context.Context, tagName string) (vos []*contentful.Asset, err error) {
if cc == nil || cc.Client == nil {
return nil, errors.New("GetAssetsByTag: No client available")
}
if !cc.cacheInit {
return nil, errors.New("GetAssetsByTag: only available with cache")
}
tags, err := cc.getAllTags(ctx, true)
if err != nil {
return nil, errors.New("GetAssetsByTag could not get tags from cache: " + err.Error())
}
cc.cacheMutex.assetsGcLock.RLock()
defer cc.cacheMutex.assetsGcLock.RUnlock()
if _, tagExists := tags[tagName]; !tagExists {
return nil, nil
}
tagID := tags[tagName]
for _, vo := range cc.Cache.assets {
for _, voTag := range vo.Metadata.Tags {
if voTag.Sys.ID == tagID {
vos = append(vos, vo)
}
}
}
return vos, nil
}



func (cc *ContentfulClient) GetAllTags(ctx context.Context) (map[string]string, error) {
return cc.getAllTags(ctx, true)
}

func (cc *ContentfulClient) GetAssetByID(ctx context.Context, id string, forceNoCache ...bool) (*contentful.Asset, error) {
if cc == nil || cc.Client == nil {
return nil, errors.New("GetAssetByID: No client available")
Expand Down Expand Up @@ -1228,6 +1268,16 @@ func (cc *ContentfulClient) UpdateCache(ctx context.Context, contentTypes []stri
}
}
}
tags, err := cc.GetAllTags(ctx)
if err != nil {
if cc.logFn != nil && cc.logLevel <= LogWarn {
cc.logFn(map[string]interface{}{"task": "UpdateCache", "error": err.Error()}, LogWarn, "failed to cache tags")
}
}
cc.cacheMutex.tagGcLock.Lock()
cc.Cache.tags = tags
cc.cacheMutex.tagGcLock.Unlock()

if isSync {
return cc.syncCache(ctxAtWork, contentTypes)
}
Expand Down Expand Up @@ -1418,7 +1468,7 @@ func (cc *ContentfulClient) cacheSpace(ctx context.Context, contentTypes []strin
parentMap: map[string][]EntryReference{},
}
if cacheAssets {
contentTypes = append([]string{assetWorkerType}, contentTypes...)
contentTypes = append([]string{assetWorkerType, tagWorkerType}, contentTypes...)
}
_, errCanWeEvenConnect := cc.Client.Spaces.Get(ctx, cc.SpaceID)
cc.cacheMutex.sharedDataGcLock.RLock()
Expand Down Expand Up @@ -1669,6 +1719,49 @@ func (cc *ContentfulClient) getAllAssets(ctx context.Context, tryCacheFirst bool
return assets, nil
}

func (cc *ContentfulClient) getAllTags(ctx context.Context, tryCacheFirst bool) (map[string]string, error) {
if cc == nil || cc.Client == nil {
return nil, errors.New("getAllTags: No client available")
}
cc.cacheMutex.sharedDataGcLock.RLock()
offline := cc.offline
cacheInit := cc.cacheInit
cc.cacheMutex.sharedDataGcLock.RUnlock()
cc.cacheMutex.tagGcLock.RLock()
defer cc.cacheMutex.tagGcLock.RUnlock()
if cacheInit && cc.Cache.tags != nil && tryCacheFirst {
return cc.Cache.tags, nil
}
allItems := []interface{}{}
tags := map[string]string{}
if offline {
for _, asset := range cc.offlineTemp.Tags {
allItems = append(allItems, asset)
}
} else {
col := cc.Client.Tags.List(ctx, cc.SpaceID)
col.Query.Limit(1000)
_, err := col.Next()
if err != nil {
return nil, err
}
allItems = col.Items
}
for _, item := range allItems {
tag := contentful.Tag{}
byt, err := json.Marshal(item)
if err != nil {
return nil, err
}
err = json.Unmarshal(byt, &tag)
if err != nil {
return nil, err
}
tags[tag.Name] = tag.Sys.ID
}
return tags, nil
}

func getOfflineSpaceFromFile(file []byte) (*offlineTemp, error) {
offlineTemp := &offlineTemp{}
err := json.Unmarshal(file, offlineTemp)
Expand Down Expand Up @@ -2149,6 +2242,16 @@ func updateCacheForContentType(ctx context.Context, results chan ContentTypeResu
if cc.logFn != nil && cc.logLevel <= LogInfo {
cc.logFn(map[string]interface{}{"contentType": "asset", "method": "updateCacheForContentType", "size": len(allAssets)}, LogInfo, InfoCachedAllAssets)
}

case tagWorkerType:
allTags, err := cc.getAllTags(ctx, false)
if err != nil {
return errors.New("updateCacheForContentType failed for tags")
}
tempCache.tags = allTags
if cc.logFn != nil && cc.logLevel <= LogInfo {
cc.logFn(map[string]interface{}{"contentType": "tag", "method": "updateCacheForContentType", "size": len(allTags)}, LogInfo, InfoCachedAllTags)
}
}
return nil
}
Expand Down
49 changes: 49 additions & 0 deletions erm/templates/contentful_vo_lib_contenttype.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,33 @@ func (cc *ContentfulClient) GetFiltered{{ firstCap $contentType.Sys.ID }}(ctx co
return {{ $contentType.Sys.ID }}Map, nil
}

func (cc *ContentfulClient) Get{{ firstCap $contentType.Sys.ID }}ByTag(ctx context.Context, tagName string) (vos []*Cf{{ firstCap $contentType.Sys.ID }}, err error) {
if cc == nil || cc.Client == nil {
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag: No client available")
}
if !cc.cacheInit {
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag: only available with cache")
}
tags, err := cc.getAllTags(ctx, true)
if err != nil {
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByTag could not get tags from cache: " + err.Error())
}
cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.RLock()
defer cc.cacheMutex.{{ $contentType.Sys.ID }}GcLock.RUnlock()
if _, tagExists := tags[tagName]; !tagExists {
return nil, nil
}
tagID := tags[tagName]
for _, vo := range cc.Cache.entryMaps.{{ $contentType.Sys.ID }} {
for _, voTag := range vo.Metadata.Tags {
if voTag.Sys.ID == tagID {
vos = append(vos, vo)
}
}
}
return vos, nil
}

func (cc *ContentfulClient) Get{{ firstCap $contentType.Sys.ID }}ByID(ctx context.Context, id string, forceNoCache ...bool) (vo *Cf{{ firstCap $contentType.Sys.ID }}, err error) {
if cc == nil || cc.Client == nil {
return nil, errors.New("Get{{ firstCap $contentType.Sys.ID }}ByID: No client available")
Expand Down Expand Up @@ -468,6 +495,28 @@ func (vo *Cf{{ firstCap $contentType.Sys.ID }}) {{ firstCap $field.ID }}(ctx con
{{ end }}
{{ end }}

func (vo *Cf{{ firstCap $contentType.Sys.ID }}) IsArchived(ctx context.Context) (bool, error) {
if vo == nil {
return false, errors.New("IsArchived: Value Object is nil")
}
if vo.CC == nil {
return false, errors.New("IsArchived: Value Object has nil Contentful client")
}
if vo.CC.clientMode != ClientModeCMA {
return false, errors.New("IsArchived: Only available in ClientModeCMA")
}
cfEntry := &contentful.Entry{}
tmp, errMarshal := json.Marshal(vo)
if errMarshal != nil {
return false, errors.New("Cf{{ firstCap $contentType.Sys.ID }} IsArchived: Can't marshal JSON from VO")
}
errUnmarshal := json.Unmarshal(tmp, &cfEntry)
if errUnmarshal != nil {
return false, errors.New("Cf{{ firstCap $contentType.Sys.ID }} IsArchived: Can't unmarshal JSON into CF entry")
}
return len(cfEntry.Sys.ArchivedAt) > 0, nil
}

// {{ firstCap $contentType.Sys.ID }} Field setters
{{ range $fieldIndex, $field := $contentType.Fields }}
func (vo *Cf{{ firstCap $contentType.Sys.ID }}) Set{{ firstCap $field.ID }}({{ $field.ID }} {{ mapFieldType $contentType.Sys.ID $field }}, locale ...Locale) (err error) {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/foomo/gocontentful
go 1.21

require (
github.com/foomo/contentful v0.5.1
github.com/foomo/contentful v0.5.3
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ github.com/aoliveti/curling v1.1.0/go.mod h1:xoDmoUg9vX3pMTltyG/rp9tFtIlweL2QeCJ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/foomo/contentful v0.5.1 h1:qQTEUAtlO5MffaXMgl5p01zIgqyoWZZH8U9YnWFO0uw=
github.com/foomo/contentful v0.5.1/go.mod h1:vdEkAQ25w3O4RjpdnIj2PDyTrErf7Snhc73HIXO5+cc=
github.com/foomo/contentful v0.5.3 h1:hZiLTjNKGJNNNamToPIY/ceXtNViRu0LPQcnlViLy8A=
github.com/foomo/contentful v0.5.3/go.mod h1:vdEkAQ25w3O4RjpdnIj2PDyTrErf7Snhc73HIXO5+cc=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
27 changes: 15 additions & 12 deletions test/testapi/gocontentfulvo.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 15 additions & 12 deletions test/testapi/gocontentfulvobase.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d981374

Please sign in to comment.