Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Tags support, Archived flag #22

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.2
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
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
Loading