Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
217
cloud/maplefile-backend/internal/repo/filemetadata/get.go
Normal file
217
cloud/maplefile-backend/internal/repo/filemetadata/get.go
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/filemetadata/get.go
|
||||
package filemetadata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
|
||||
)
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) Get(id gocql.UUID) (*dom_file.File, error) {
|
||||
var (
|
||||
collectionID, ownerID, createdByUserID, modifiedByUserID gocql.UUID
|
||||
encryptedMetadata, encryptedKeyJSON, encryptionVersion string
|
||||
encryptedHash, encryptedFileObjectKey string
|
||||
encryptedThumbnailObjectKey string
|
||||
encryptedFileSizeInBytes, encryptedThumbnailSizeInBytes int64
|
||||
tagsJSON string
|
||||
createdAt, modifiedAt, tombstoneExpiry time.Time
|
||||
version, tombstoneVersion uint64
|
||||
state string
|
||||
)
|
||||
|
||||
query := `SELECT id, collection_id, owner_id, encrypted_metadata, encrypted_file_key,
|
||||
encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
|
||||
encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes, tags,
|
||||
created_at, created_by_user_id, modified_at, modified_by_user_id, version,
|
||||
state, tombstone_version, tombstone_expiry
|
||||
FROM maplefile.files_by_id WHERE id = ?`
|
||||
|
||||
err := impl.Session.Query(query, id).Scan(
|
||||
&id, &collectionID, &ownerID, &encryptedMetadata, &encryptedKeyJSON,
|
||||
&encryptionVersion, &encryptedHash, &encryptedFileObjectKey, &encryptedFileSizeInBytes,
|
||||
&encryptedThumbnailObjectKey, &encryptedThumbnailSizeInBytes, &tagsJSON,
|
||||
&createdAt, &createdByUserID, &modifiedAt, &modifiedByUserID, &version,
|
||||
&state, &tombstoneVersion, &tombstoneExpiry)
|
||||
|
||||
if err != nil {
|
||||
if err == gocql.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get file: %w", err)
|
||||
}
|
||||
|
||||
// Deserialize encrypted file key
|
||||
encryptedFileKey, err := impl.deserializeEncryptedFileKey(encryptedKeyJSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize encrypted file key: %w", err)
|
||||
}
|
||||
|
||||
// Deserialize tags
|
||||
tags, err := impl.deserializeTags(tagsJSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize tags: %w", err)
|
||||
}
|
||||
|
||||
file := &dom_file.File{
|
||||
ID: id,
|
||||
CollectionID: collectionID,
|
||||
OwnerID: ownerID,
|
||||
EncryptedMetadata: encryptedMetadata,
|
||||
EncryptedFileKey: encryptedFileKey,
|
||||
EncryptionVersion: encryptionVersion,
|
||||
EncryptedHash: encryptedHash,
|
||||
EncryptedFileObjectKey: encryptedFileObjectKey,
|
||||
EncryptedFileSizeInBytes: encryptedFileSizeInBytes,
|
||||
EncryptedThumbnailObjectKey: encryptedThumbnailObjectKey,
|
||||
EncryptedThumbnailSizeInBytes: encryptedThumbnailSizeInBytes,
|
||||
Tags: tags,
|
||||
CreatedAt: createdAt,
|
||||
CreatedByUserID: createdByUserID,
|
||||
ModifiedAt: modifiedAt,
|
||||
ModifiedByUserID: modifiedByUserID,
|
||||
Version: version,
|
||||
State: state,
|
||||
TombstoneVersion: tombstoneVersion,
|
||||
TombstoneExpiry: tombstoneExpiry,
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) GetByIDs(ids []gocql.UUID) ([]*dom_file.File, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*dom_file.File{}, nil
|
||||
}
|
||||
|
||||
// Use a buffered channel to collect results from goroutines
|
||||
resultsChan := make(chan *dom_file.File, len(ids))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Launch a goroutine for each ID lookup
|
||||
for _, id := range ids {
|
||||
wg.Add(1)
|
||||
go func(id gocql.UUID) {
|
||||
defer wg.Done()
|
||||
|
||||
// Call the existing state-aware Get method
|
||||
file, err := impl.Get(id)
|
||||
|
||||
if err != nil {
|
||||
impl.Logger.Warn("failed to get file by ID",
|
||||
zap.String("file_id", id.String()),
|
||||
zap.Error(err))
|
||||
// Send nil on error to indicate failure/absence for this ID
|
||||
resultsChan <- nil
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns nil for ErrNotFound or inactive state when stateAware is true.
|
||||
// Send the potentially nil file result to the channel.
|
||||
resultsChan <- file
|
||||
|
||||
}(id) // Pass id into the closure
|
||||
}
|
||||
|
||||
// Goroutine to close the channel once all workers are done
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultsChan)
|
||||
}()
|
||||
|
||||
// Collect results from the channel
|
||||
var files []*dom_file.File
|
||||
for file := range resultsChan {
|
||||
// Only append non-nil files (found and active)
|
||||
if file != nil {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
// The original function logs warnings for errors but doesn't return an error
|
||||
// from GetByIDs itself. We maintain this behavior.
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) GetByCollection(collectionID gocql.UUID) ([]*dom_file.File, error) {
|
||||
var fileIDs []gocql.UUID
|
||||
|
||||
query := `SELECT id FROM maplefile.files_by_collection
|
||||
WHERE collection_id = ?`
|
||||
|
||||
iter := impl.Session.Query(query, collectionID).Iter()
|
||||
|
||||
var fileID gocql.UUID
|
||||
for iter.Scan(&fileID) {
|
||||
fileIDs = append(fileIDs, fileID)
|
||||
}
|
||||
|
||||
if err := iter.Close(); err != nil {
|
||||
return nil, fmt.Errorf("failed to get files by collection: %w", err)
|
||||
}
|
||||
|
||||
return impl.loadMultipleFiles(fileIDs)
|
||||
}
|
||||
|
||||
func (impl *fileMetadataRepositoryImpl) loadMultipleFiles(fileIDs []gocql.UUID) ([]*dom_file.File, error) {
|
||||
if len(fileIDs) == 0 {
|
||||
return []*dom_file.File{}, nil
|
||||
}
|
||||
|
||||
// Use a buffered channel to collect results from goroutines
|
||||
// We expect up to len(fileIDs) results, some of which might be nil.
|
||||
resultsChan := make(chan *dom_file.File, len(fileIDs))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Launch a goroutine for each ID lookup
|
||||
for _, id := range fileIDs {
|
||||
wg.Add(1)
|
||||
go func(id gocql.UUID) {
|
||||
defer wg.Done()
|
||||
|
||||
// Call the existing state-aware Get method
|
||||
// This method returns nil if the file is not found, or if it's
|
||||
// found but not in the 'active' state.
|
||||
file, err := impl.Get(id)
|
||||
|
||||
if err != nil {
|
||||
// Log the error but continue processing other IDs.
|
||||
impl.Logger.Warn("failed to load file",
|
||||
zap.String("file_id", id.String()),
|
||||
zap.Error(err))
|
||||
// Send nil on error, consistent with how Get returns nil for not found/inactive.
|
||||
resultsChan <- nil
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns nil for ErrNotFound or inactive state when stateAware is true.
|
||||
// Send the potentially nil file result to the channel.
|
||||
resultsChan <- file
|
||||
|
||||
}(id) // Pass id into the closure
|
||||
}
|
||||
|
||||
// Goroutine to close the channel once all workers are done
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultsChan)
|
||||
}()
|
||||
|
||||
// Collect results from the channel
|
||||
var files []*dom_file.File
|
||||
for file := range resultsChan {
|
||||
// Only append non-nil files (found and active, or found but error logged)
|
||||
if file != nil {
|
||||
files = append(files, file)
|
||||
}
|
||||
}
|
||||
|
||||
// The original function logged warnings for errors but didn't return an error
|
||||
// from loadMultipleFiles itself. We maintain this behavior.
|
||||
return files, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue