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  }