github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/metadata/metadata.go (about) 1 // Package metadata is used for manipulating the cozyMetadata field of the 2 // documents. 3 package metadata 4 5 import ( 6 "errors" 7 "time" 8 ) 9 10 // MetadataVersion represents the CozyMetadata version used. 11 const MetadataVersion = 1 12 13 // ErrSlugEmpty is returned when an UpdatedByApp entry is created with and empty 14 // slug 15 var ErrSlugEmpty = errors.New("Slug cannot be empty") 16 17 // UpdatedByAppEntry represents a modification made by an application to the 18 // document 19 type UpdatedByAppEntry struct { 20 Slug string `json:"slug"` // Slug of the app 21 Date time.Time `json:"date"` // Date of the update 22 Version string `json:"version,omitempty"` // Version identifier of the app 23 Instance string `json:"instance,omitempty"` // URL of the instance 24 } 25 26 // CozyMetadata holds all the metadata of a document 27 type CozyMetadata struct { 28 // Name or identifier for the version of the schema used by this document 29 DocTypeVersion string `json:"doctypeVersion"` 30 // Version of the cozyMetadata 31 MetadataVersion int `json:"metadataVersion"` 32 // Tell if the document if part of the favorites 33 Favorite bool `json:"favorite,omitempty"` 34 // Creation date of the cozy document 35 CreatedAt time.Time `json:"createdAt"` 36 // Slug of the app or konnector which created the document 37 CreatedByApp string `json:"createdByApp,omitempty"` 38 // Version identifier of the app 39 CreatedByAppVersion string `json:"createdByAppVersion,omitempty"` 40 // Last modification date of the cozy document 41 UpdatedAt time.Time `json:"updatedAt"` 42 // List of objects representing the applications which modified the cozy document 43 UpdatedByApps []*UpdatedByAppEntry `json:"updatedByApps,omitempty"` 44 // Identifier of the account in io.cozy.accounts (for konnectors) 45 SourceAccount string `json:"sourceAccount,omitempty"` 46 // Identifier unique to the account targeted by the connector (login most of the time) 47 SourceIdentifier string `json:"sourceAccountIdentifier,omitempty"` 48 } 49 50 // New initializes a new CozyMetadata structure 51 func New() *CozyMetadata { 52 now := time.Now() 53 return &CozyMetadata{ 54 MetadataVersion: MetadataVersion, 55 CreatedAt: now, 56 UpdatedAt: now, 57 } 58 } 59 60 // NewWithApp initializes a CozyMetadata with a slug and a version 61 // Version is optional 62 func NewWithApp(slug, version, doctypeVersion string) (*CozyMetadata, error) { 63 if slug == "" { 64 return nil, ErrSlugEmpty 65 } 66 md := New() 67 md.CreatedByApp = slug 68 if version != "" { 69 md.CreatedByAppVersion = version 70 } 71 md.DocTypeVersion = doctypeVersion 72 73 err := md.UpdatedByApp(slug, version) 74 if err != nil { 75 return nil, err 76 } 77 return md, nil 78 } 79 80 // Clone clones a CozyMetadata struct 81 func (cm *CozyMetadata) Clone() *CozyMetadata { 82 cloned := *cm 83 cloned.UpdatedByApps = make([]*UpdatedByAppEntry, len(cm.UpdatedByApps)) 84 copy(cloned.UpdatedByApps, cm.UpdatedByApps) 85 return &cloned 86 } 87 88 // EnsureCreatedFields ensures that empty fields are filled, otherwise use 89 // the default metadata values during the creation process 90 func (cm *CozyMetadata) EnsureCreatedFields(defaultMetadata *CozyMetadata) { 91 if cm.UpdatedAt.IsZero() { 92 cm.UpdatedAt = defaultMetadata.UpdatedAt 93 } 94 if cm.CreatedByApp == "" { 95 cm.CreatedByApp = defaultMetadata.CreatedByApp 96 } 97 if cm.DocTypeVersion == "" { 98 cm.DocTypeVersion = defaultMetadata.DocTypeVersion 99 } 100 if cm.MetadataVersion == 0 { 101 cm.MetadataVersion = defaultMetadata.MetadataVersion 102 } 103 if cm.UpdatedByApps == nil { 104 cm.UpdatedByApps = defaultMetadata.UpdatedByApps 105 } 106 } 107 108 // ChangeUpdatedAt updates the UpdatedAt timestamp 109 func (cm *CozyMetadata) ChangeUpdatedAt() { 110 cm.UpdatedAt = time.Now() 111 } 112 113 // UpdatedByApp updates an entry either by updating the struct if the 114 // slug/version already exists or by appending a new entry to the list 115 func (cm *CozyMetadata) UpdatedByApp(slug, version string) error { 116 if slug == "" { 117 return ErrSlugEmpty 118 } 119 120 now := time.Now() 121 cm.UpdatedAt = now 122 updated := &UpdatedByAppEntry{Slug: slug, Date: now, Version: version} 123 for i, entry := range cm.UpdatedByApps { 124 if entry.Slug == slug { 125 cm.UpdatedByApps[i] = updated 126 return nil 127 } 128 } 129 130 // The entry has not been found, adding it 131 cm.UpdatedByApps = append(cm.UpdatedByApps, updated) 132 return nil 133 }