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

Add SendableItemMetadata #44

Merged
merged 27 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a890002
Replace apply with just taking metadata on init in DirectoryReadConve…
claucambra Jan 20, 2025
d5eb5a1
Mark completionHandler for thumbnailFetching as sendable
claucambra Jan 20, 2025
780f7bb
Make directory read NKFile to ItemMetadata conversion Sendable approved
claucambra Jan 20, 2025
8af4103
Exclusively use SendableItemMetadata outside of the database manager
claucambra Jan 20, 2025
2186ce8
Only use sendable item metadata as argument/return type for public da…
claucambra Jan 21, 2025
6435647
Rename ItemMetadata to RealmItemMetadata
claucambra Jan 21, 2025
5c9561c
Ensure more public db manager methods return sendable item metadata
claucambra Jan 21, 2025
b9f5892
Just use Item rootContainer method to get the root container metadata
claucambra Jan 21, 2025
f9d641d
When modifying property values of metadatas, declare the variable usi…
claucambra Jan 21, 2025
9903223
Move Status and SharePermissions enum out of RealmItemMetadata
claucambra Jan 21, 2025
100bd06
Allow return of nil in toDirectoryReadMetadatas
claucambra Jan 21, 2025
e7c957f
Split RealmItemMetadata and SendableItemMetadata
claucambra Jan 21, 2025
ed0273b
Rename RealmItemMetadata.swift to ItemMetadata.swift
claucambra Jan 21, 2025
a5ce84a
Remove unused properties in ItemMetadata
claucambra Jan 21, 2025
bf67087
Add default arguments for frequently placeholdered values in Sendable…
claucambra Jan 21, 2025
4a7cc5e
Use MockRemoteItem-s toItemMetadata where applicable
claucambra Jan 21, 2025
a1d8831
Make testing SendableItemMetadata constructor more convenient
claucambra Jan 21, 2025
c88cc02
Make FilesDatabaseManager sendable
claucambra Jan 22, 2025
0d67db5
Properly apply lock state values on conversion of MockRemoteItem
claucambra Jan 22, 2025
21ac67e
Apply more default values in sendableitemmetadata constructor, for lo…
claucambra Jan 22, 2025
a038a0c
Further default arguments for SendableItemMetadata
claucambra Jan 22, 2025
78c8294
Make lock related properties optional if no lock is available
claucambra Jan 22, 2025
15affb5
Make chunkUploadId a nullable property
claucambra Jan 22, 2025
515f410
Make session related properties nullable
claucambra Jan 22, 2025
001d0c6
Add missing properties to ItemMetadata
claucambra Jan 22, 2025
f3b284b
Store share permissions cloud mesh
claucambra Jan 22, 2025
0c68266
Mark RealmItemMetadata as internal
claucambra Jan 22, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,48 @@ import OSLog
import RealmSwift

extension FilesDatabaseManager {
func childItems(directoryMetadata: ItemMetadata) -> Results<ItemMetadata> {
var directoryServerUrl: String
if directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue {
directoryServerUrl = directoryMetadata.serverUrl
private func fullServerPathUrl(for metadata: any ItemMetadata) -> String {
if metadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue {
metadata.serverUrl
} else {
directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
metadata.serverUrl + "/" + metadata.fileName
}
return itemMetadatas.where { $0.serverUrl.starts(with: directoryServerUrl) }
}

public func childItemCount(directoryMetadata: ItemMetadata) -> Int {
childItems(directoryMetadata: directoryMetadata).count
public func childItems(directoryMetadata: SendableItemMetadata) -> [SendableItemMetadata] {
let directoryServerUrl = fullServerPathUrl(for: directoryMetadata)
return itemMetadatas
.where({ $0.serverUrl.starts(with: directoryServerUrl) })
.toUnmanagedResults()
}

public func childItemCount(directoryMetadata: SendableItemMetadata) -> Int {
let directoryServerUrl = fullServerPathUrl(for: directoryMetadata)
return itemMetadatas
.where({ $0.serverUrl.starts(with: directoryServerUrl) })
.count
}

public func parentDirectoryMetadataForItem(_ itemMetadata: ItemMetadata) -> ItemMetadata? {
self.itemMetadata(account: itemMetadata.account, locatedAtRemoteUrl: itemMetadata.serverUrl)
public func parentDirectoryMetadataForItem(
_ itemMetadata: SendableItemMetadata
) -> SendableItemMetadata? {
self.itemMetadata(
account: itemMetadata.account, locatedAtRemoteUrl: itemMetadata.serverUrl
)
}

public func directoryMetadata(ocId: String) -> ItemMetadata? {
public func directoryMetadata(ocId: String) -> SendableItemMetadata? {
if let metadata = itemMetadatas.where({ $0.ocId == ocId && $0.directory }).first {
return ItemMetadata(value: metadata)
return SendableItemMetadata(value: metadata)
}

return nil
}

// Deletes all metadatas related to the info of the directory provided
public func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) -> [ItemMetadata]? {
let database = ncDatabase()
public func deleteDirectoryAndSubdirectoriesMetadata(
ocId: String
) -> [SendableItemMetadata]? {
guard let directoryMetadata = itemMetadatas
.where({ $0.ocId == ocId && $0.directory })
.first
Expand All @@ -60,7 +73,7 @@ extension FilesDatabaseManager {
return nil
}

let directoryMetadataCopy = ItemMetadata(value: directoryMetadata)
let directoryMetadataCopy = SendableItemMetadata(value: directoryMetadata)
let directoryOcId = directoryMetadata.ocId
let directoryUrlPath = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
let directoryAccount = directoryMetadata.account
Expand All @@ -75,6 +88,7 @@ extension FilesDatabaseManager {
"""
)

let database = ncDatabase()
do {
try database.write { database.delete(directoryMetadata) }
} catch let error {
Expand All @@ -90,14 +104,14 @@ extension FilesDatabaseManager {
return nil
}

var deletedMetadatas: [ItemMetadata] = [directoryMetadataCopy]
var deletedMetadatas: [SendableItemMetadata] = [directoryMetadataCopy]

let results = itemMetadatas.where {
$0.account == directoryAccount && $0.serverUrl.starts(with: directoryUrlPath)
}

for result in results {
let inactiveItemMetadata = ItemMetadata(value: result)
let inactiveItemMetadata = SendableItemMetadata(value: result)
do {
try database.write { database.delete(result) }
deletedMetadatas.append(inactiveItemMetadata)
Expand Down Expand Up @@ -128,7 +142,7 @@ extension FilesDatabaseManager {

public func renameDirectoryAndPropagateToChildren(
ocId: String, newServerUrl: String, newFileName: String
) -> [ItemMetadata]? {
) -> [SendableItemMetadata]? {
guard let directoryMetadata = itemMetadatas
.where({ $0.ocId == ocId && $0.directory })
.first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import RealmSwift

extension FilesDatabaseManager {
func trashedItemMetadatas(account: Account) -> [ItemMetadata] {
func trashedItemMetadatas(account: Account) -> [SendableItemMetadata] {
ncDatabase()
.objects(ItemMetadata.self)
.objects(RealmItemMetadata.self)
.where {
$0.account == account.ncKitAccount && $0.serverUrl.starts(with: account.trashUrl)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import RealmSwift
fileprivate let stable1_0SchemaVersion: UInt64 = 100
fileprivate let stable2_0SchemaVersion: UInt64 = 200 // Major change: deleted LocalFileMetadata type

public class FilesDatabaseManager {
public final class FilesDatabaseManager: Sendable {
public static let shared = FilesDatabaseManager()!

private static let relativeDatabaseFolderPath = "Database/"
Expand All @@ -29,7 +29,7 @@ public class FilesDatabaseManager {

static let logger = Logger(subsystem: Logger.subsystem, category: "filesdatabase")

var itemMetadatas: Results<ItemMetadata> { ncDatabase().objects(ItemMetadata.self) }
var itemMetadatas: Results<RealmItemMetadata> { ncDatabase().objects(RealmItemMetadata.self) }

public init(realmConfig: Realm.Configuration = Realm.Configuration.defaultConfiguration) {
Realm.Configuration.defaultConfiguration = realmConfig
Expand Down Expand Up @@ -78,7 +78,7 @@ public class FilesDatabaseManager {
localFileMetadataOcIds.insert(lfmOcId)
}

migration.enumerateObjects(ofType: ItemMetadata.className()) { _, newObject in
migration.enumerateObjects(ofType: RealmItemMetadata.className()) { _, newObject in
guard let newObject,
let imOcId = newObject["ocId"] as? String,
localFileMetadataOcIds.contains(imOcId)
Expand All @@ -89,7 +89,7 @@ public class FilesDatabaseManager {
}

},
objectTypes: [ItemMetadata.self, RemoteFileChunk.self]
objectTypes: [RealmItemMetadata.self, RemoteFileChunk.self]
)
self.init(realmConfig: config)
}
Expand All @@ -106,21 +106,20 @@ public class FilesDatabaseManager {
!itemMetadatas.where({ $0.account == account }).isEmpty
}

public func itemMetadata(ocId: String, managed: Bool = false) -> ItemMetadata? {
public func itemMetadata(ocId: String) -> SendableItemMetadata? {
// Realm objects are live-fire, i.e. they will be changed and invalidated according to
// changes in the db.
//
// Let's therefore create a copy
if let itemMetadata = itemMetadatas.where({ $0.ocId == ocId }).first {
return managed ? itemMetadata : ItemMetadata(value: itemMetadata)
return SendableItemMetadata(value: itemMetadata)
}

return nil
}

public func itemMetadata(
account: String, locatedAtRemoteUrl remoteUrl: String // Is the URL for the actual item
) -> ItemMetadata? {
) -> SendableItemMetadata? {
guard let actualRemoteUrl = URL(string: remoteUrl) else { return nil }
let fileName = actualRemoteUrl.lastPathComponent
guard var serverUrl = actualRemoteUrl
Expand All @@ -134,38 +133,40 @@ public class FilesDatabaseManager {
if let metadata = itemMetadatas.where({
$0.account == account && $0.serverUrl == serverUrl && $0.fileName == fileName
}).first {
return ItemMetadata(value: metadata)
return SendableItemMetadata(value: metadata)
}
return nil
}

public func itemMetadatas(account: String) -> [ItemMetadata] {
public func itemMetadatas(account: String) -> [SendableItemMetadata] {
itemMetadatas
.where { $0.account == account }
.toUnmanagedResults()
}

public func itemMetadatas(account: String, underServerUrl serverUrl: String) -> [ItemMetadata] {
public func itemMetadatas(
account: String, underServerUrl serverUrl: String
) -> [SendableItemMetadata] {
itemMetadatas
.where { $0.account == account && $0.serverUrl.starts(with: serverUrl) }
.toUnmanagedResults()
}

public func itemMetadataFromFileProviderItemIdentifier(
_ identifier: NSFileProviderItemIdentifier
) -> ItemMetadata? {
) -> SendableItemMetadata? {
itemMetadata(ocId: identifier.rawValue)
}

private func processItemMetadatasToDelete(
existingMetadatas: Results<ItemMetadata>,
updatedMetadatas: [ItemMetadata]
) -> [ItemMetadata] {
var deletedMetadatas: [ItemMetadata] = []
existingMetadatas: Results<RealmItemMetadata>,
updatedMetadatas: [SendableItemMetadata]
) -> [RealmItemMetadata] {
var deletedMetadatas: [RealmItemMetadata] = []

for existingMetadata in existingMetadatas {
guard !updatedMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
let metadataToDelete = itemMetadata(ocId: existingMetadata.ocId, managed: true)
let metadataToDelete = itemMetadatas.where({ $0.ocId == existingMetadata.ocId }).first
else { continue }

deletedMetadatas.append(metadataToDelete)
Expand All @@ -179,22 +180,23 @@ public class FilesDatabaseManager {
}

private func processItemMetadatasToUpdate(
existingMetadatas: Results<ItemMetadata>,
updatedMetadatas: [ItemMetadata],
existingMetadatas: Results<RealmItemMetadata>,
updatedMetadatas: [SendableItemMetadata],
updateDirectoryEtags: Bool
) -> (
newMetadatas: [ItemMetadata], updatedMetadatas: [ItemMetadata],
directoriesNeedingRename: [ItemMetadata]
newMetadatas: [SendableItemMetadata],
updatedMetadatas: [SendableItemMetadata],
directoriesNeedingRename: [SendableItemMetadata]
) {
var returningNewMetadatas: [ItemMetadata] = []
var returningUpdatedMetadatas: [ItemMetadata] = []
var directoriesNeedingRename: [ItemMetadata] = []
var returningNewMetadatas: [SendableItemMetadata] = []
var returningUpdatedMetadatas: [SendableItemMetadata] = []
var directoriesNeedingRename: [SendableItemMetadata] = []

for updatedMetadata in updatedMetadatas {
for var updatedMetadata in updatedMetadatas {
if let existingMetadata = existingMetadatas.first(where: {
$0.ocId == updatedMetadata.ocId
}) {
if existingMetadata.status == ItemMetadata.Status.normal.rawValue,
if existingMetadata.status == Status.normal.rawValue,
!existingMetadata.isInSameDatabaseStoreableRemoteState(updatedMetadata)
{
if updatedMetadata.directory {
Expand Down Expand Up @@ -254,29 +256,29 @@ public class FilesDatabaseManager {
public func updateItemMetadatas(
account: String,
serverUrl: String,
updatedMetadatas: [ItemMetadata],
updatedMetadatas: [SendableItemMetadata],
updateDirectoryEtags: Bool
) -> (
newMetadatas: [ItemMetadata]?,
updatedMetadatas: [ItemMetadata]?,
deletedMetadatas: [ItemMetadata]?
newMetadatas: [SendableItemMetadata]?,
updatedMetadatas: [SendableItemMetadata]?,
deletedMetadatas: [SendableItemMetadata]?
) {
let database = ncDatabase()

do {
let existingMetadatas = database
.objects(ItemMetadata.self)
.objects(RealmItemMetadata.self)
.where {
$0.account == account &&
$0.serverUrl == serverUrl &&
$0.status == ItemMetadata.Status.normal.rawValue
$0.status == Status.normal.rawValue
}

// NOTE: These metadatas are managed -- be careful!
let metadatasToDelete = processItemMetadatasToDelete(
existingMetadatas: existingMetadatas,
updatedMetadatas: updatedMetadatas)
let metadatasToDeleteCopy = metadatasToDelete.map { ItemMetadata(value: $0) }
let metadatasToDeleteCopy = metadatasToDelete.map { SendableItemMetadata(value: $0) }

let metadatasToChange = processItemMetadatasToUpdate(
existingMetadatas: existingMetadatas,
Expand All @@ -299,15 +301,11 @@ public class FilesDatabaseManager {

try database.write {
database.delete(metadatasToDelete)
database.add(metadatasToUpdate.map { ItemMetadata(value: $0) }, update: .modified)
database.add(metadatasToCreate.map { ItemMetadata(value: $0) }, update: .all)
database.add(metadatasToUpdate.map { RealmItemMetadata(value: $0) }, update: .modified)
database.add(metadatasToCreate.map { RealmItemMetadata(value: $0) }, update: .all)
}

return (
newMetadatas: metadatasToCreate,
updatedMetadatas: metadatasToUpdate,
deletedMetadatas: metadatasToDeleteCopy
)
return (metadatasToCreate, metadatasToUpdate, metadatasToDeleteCopy)
} catch {
Self.logger.error(
"""
Expand All @@ -322,8 +320,8 @@ public class FilesDatabaseManager {
// If setting a downloading or uploading status, also modified the relevant boolean properties
// of the item metadata object
public func setStatusForItemMetadata(
_ metadata: ItemMetadata, status: ItemMetadata.Status
) -> ItemMetadata? {
_ metadata: SendableItemMetadata, status: Status
) -> SendableItemMetadata? {
guard let result = itemMetadatas.where({ $0.ocId == metadata.ocId }).first else {
Self.logger.debug(
"""
Expand All @@ -344,7 +342,7 @@ public class FilesDatabaseManager {
result.uploaded = false
result.chunkUploadId = UUID().uuidString
} else if status == .normal, metadata.isUpload {
result.chunkUploadId = ""
result.chunkUploadId = nil
}

Self.logger.debug(
Expand All @@ -356,7 +354,7 @@ public class FilesDatabaseManager {
"""
)
}
return ItemMetadata(value: result)
return SendableItemMetadata(value: result)
} catch {
Self.logger.error(
"""
Expand All @@ -372,12 +370,12 @@ public class FilesDatabaseManager {
return nil
}

public func addItemMetadata(_ metadata: ItemMetadata) {
public func addItemMetadata(_ metadata: SendableItemMetadata) {
let database = ncDatabase()

do {
try database.write {
database.add(ItemMetadata(value: metadata), update: .all)
database.add(RealmItemMetadata(value: metadata), update: .all)
Self.logger.debug(
"""
Added item metadata.
Expand All @@ -391,7 +389,7 @@ public class FilesDatabaseManager {
date: \(metadata.date, privacy: .public)
lock: \(metadata.lock, privacy: .public)
lockTimeOut: \(metadata.lockTimeOut?.description ?? "", privacy: .public)
lockOwner: \(metadata.lockOwner, privacy: .public)
lockOwner: \(metadata.lockOwner ?? "", privacy: .public)
permissions: \(metadata.permissions, privacy: .public)
size: \(metadata.size, privacy: .public)
trashbinFileName: \(metadata.trashbinFileName, privacy: .public)
Expand Down Expand Up @@ -476,7 +474,7 @@ public class FilesDatabaseManager {
}

public func parentItemIdentifierFromMetadata(
_ metadata: ItemMetadata
_ metadata: SendableItemMetadata
) -> NSFileProviderItemIdentifier? {
let homeServerFilesUrl = metadata.urlBase + Account.webDavFilesUrlSuffix + metadata.userId
let trashServerFilesUrl = metadata.urlBase + Account.webDavTrashUrlSuffix + metadata.userId + "/trash"
Expand Down
Loading