From 553ee73a11f2aeed56b5992b664647b199b90876 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 23 Jun 2020 16:12:13 +0200 Subject: [PATCH 1/9] Implement ReadRemoteFolder using new architecture --- owncloud-android-library | 2 +- .../RemoteDataSourceModule.kt | 4 +- .../dependecyinjection/UseCaseModule.kt | 4 ++ .../SynchronizeFolderOperation.java | 7 +-- .../viewmodels/drawer/DrawerViewModel.kt | 2 - .../files/datasources/RemoteFileDataSource.kt | 4 ++ .../implementation/OCRemoteFileDataSource.kt | 9 +++- .../datasources/mapper/RemoteFileMapper.kt | 44 ++++++++++++++++++ .../data/files/repository/OCFileRepository.kt | 5 ++ .../android/domain/files/FileRepository.kt | 4 ++ .../RefreshFolderFromServerAsyncUseCase.kt | 34 ++++++++++++++ .../android/domain/files/model/OCFile.kt | 46 +++++++++++++++++++ 12 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt diff --git a/owncloud-android-library b/owncloud-android-library index 23a38a7fe6e..afb064c5dcb 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 23a38a7fe6e2f59d98062a1ac48f5a3a9e200543 +Subproject commit afb064c5dcbbd71cab38fa1aceb2e0010ad2683b diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt index a6ee427a519..ce898187d5b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt @@ -29,6 +29,7 @@ import com.owncloud.android.data.capabilities.datasources.implementation.OCRemot import com.owncloud.android.data.capabilities.datasources.mapper.RemoteCapabilityMapper import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.data.files.datasources.implementation.OCRemoteFileDataSource +import com.owncloud.android.data.files.datasources.mapper.RemoteFileMapper import com.owncloud.android.data.server.datasources.RemoteServerInfoDataSource import com.owncloud.android.data.server.datasources.implementation.OCRemoteServerInfoDataSource import com.owncloud.android.data.sharing.sharees.datasources.RemoteShareeDataSource @@ -71,7 +72,7 @@ val remoteDataSourceModule = module { factory { OCRemoteAuthenticationDataSource(androidContext(), get()) } factory { OCRemoteCapabilitiesDataSource(get(), get()) } - factory { OCRemoteFileDataSource(get()) } + factory { OCRemoteFileDataSource(get(), get()) } factory { OCRemoteServerInfoDataSource(get()) } factory { OCRemoteShareDataSource(get(), get()) } factory { OCRemoteShareeDataSource(get()) } @@ -80,6 +81,7 @@ val remoteDataSourceModule = module { ).toInt()) } factory { RemoteCapabilityMapper() } + factory { RemoteFileMapper() } factory { RemoteShareMapper() } factory { RemoteUserAvatarMapper() } factory { RemoteUserInfoMapper() } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 3f0fbe5c3d1..16180dffc08 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -26,6 +26,7 @@ import com.owncloud.android.domain.authentication.usecases.SupportsOAuth2UseCase import com.owncloud.android.domain.capabilities.usecases.GetCapabilitiesAsLiveDataUseCase import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase import com.owncloud.android.domain.capabilities.usecases.RefreshCapabilitiesFromServerAsyncUseCase +import com.owncloud.android.domain.files.RefreshFolderFromServerAsyncUseCase import com.owncloud.android.domain.server.usecases.GetServerInfoAsyncUseCase import com.owncloud.android.domain.sharing.sharees.GetShareesAsyncUseCase import com.owncloud.android.domain.sharing.shares.usecases.CreatePrivateShareAsyncUseCase @@ -54,6 +55,9 @@ val useCaseModule = module { factory { GetStoredCapabilitiesUseCase(get()) } factory { RefreshCapabilitiesFromServerAsyncUseCase(get()) } + // Files + factory { RefreshFolderFromServerAsyncUseCase(get()) } + // Sharing factory { GetShareesAsyncUseCase(get()) } factory { GetSharesAsLiveDataUseCase(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index fb0f8a4fbd2..2a8440e08f9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -33,10 +33,12 @@ import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.OperationCancelledException; +import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; import com.owncloud.android.lib.resources.files.RemoteFile; +import com.owncloud.android.lib.resources.files.services.implementation.OCFileService; import com.owncloud.android.operations.common.SyncOperation; import com.owncloud.android.services.OperationsService; import com.owncloud.android.utils.FileStorageUtils; @@ -259,9 +261,8 @@ private RemoteOperationResult> fetchRemoteFolder(OwnCloudC if (mCancellationRequested.get()) { throw new OperationCancelledException(); } - - ReadRemoteFolderOperation readFolderOperation = new ReadRemoteFolderOperation(mRemotePath); - return readFolderOperation.execute(client); + OCFileService ocFileService = new OCFileService(client); + return ocFileService.refreshFolder(mRemotePath); } /** diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/drawer/DrawerViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/drawer/DrawerViewModel.kt index eb6e11d2b88..304246dda8f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/drawer/DrawerViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/drawer/DrawerViewModel.kt @@ -21,12 +21,10 @@ package com.owncloud.android.presentation.viewmodels.drawer import android.accounts.Account -import android.accounts.AccountManager import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.ViewModel -import com.owncloud.android.MainApp import com.owncloud.android.authentication.AccountUtils import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/RemoteFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/RemoteFileDataSource.kt index e8102665183..458bd32573e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/RemoteFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/RemoteFileDataSource.kt @@ -19,6 +19,10 @@ package com.owncloud.android.data.files.datasources +import com.owncloud.android.domain.files.model.OCFile + interface RemoteFileDataSource { fun checkPathExistence(path: String, checkUserCredentials: Boolean): Boolean + + fun refreshFolder(remotePath: String): List } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCRemoteFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCRemoteFileDataSource.kt index 0cc3f35d62b..8fca0003814 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCRemoteFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCRemoteFileDataSource.kt @@ -20,12 +20,19 @@ package com.owncloud.android.data.files.datasources.implementation import com.owncloud.android.data.files.datasources.RemoteFileDataSource +import com.owncloud.android.data.files.datasources.mapper.RemoteFileMapper +import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.lib.resources.files.services.FileService class OCRemoteFileDataSource( - private val fileService: FileService + private val fileService: FileService, + private val remoteFileMapper: RemoteFileMapper ) : RemoteFileDataSource { override fun checkPathExistence(path: String, checkUserCredentials: Boolean): Boolean = fileService.checkPathExistence(path = path, isUserLogged = checkUserCredentials).data + override fun refreshFolder(remotePath: String): List = + // Assert not null, service should return an empty list if no files there. + fileService.refreshFolder(remotePath).data.map { remoteFileMapper.toModel(it)!! } + } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt new file mode 100644 index 00000000000..b6e840bcb1a --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt @@ -0,0 +1,44 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.data.files.datasources.mapper + +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.mappers.RemoteMapper +import com.owncloud.android.lib.resources.files.RemoteFile + +class RemoteFileMapper : RemoteMapper { + override fun toModel(remote: RemoteFile?): OCFile? = + remote?.let { + OCFile( + remoteId = it.remoteId, + remotePath = it.remotePath, + length = it.length, + creationTimestamp = it.creationTimestamp, + modifiedTimestamp = it.modifiedTimestamp, + mimeType = it.mimeType, + etag = it.etag, + permissions = it.permissions, + privateLink = it.privateLink + ) + } + + // Not needed + override fun toRemote(model: OCFile?): RemoteFile? = null + +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 3425348f6eb..5ebc483a91e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -21,10 +21,15 @@ package com.owncloud.android.data.files.repository import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.domain.files.FileRepository +import com.owncloud.android.domain.files.model.OCFile class OCFileRepository( private val remoteFileDataSource: RemoteFileDataSource ) : FileRepository { override fun checkPathExistence(path: String, userLogged: Boolean): Boolean = remoteFileDataSource.checkPathExistence(path, userLogged) + + override fun refreshFolder(remotePath: String): List { + return remoteFileDataSource.refreshFolder(remotePath) + } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt index 249b3bcbeca..a69e71c293a 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt @@ -19,6 +19,10 @@ package com.owncloud.android.domain.files +import com.owncloud.android.domain.files.model.OCFile + interface FileRepository { fun checkPathExistence(path: String, userLogged: Boolean): Boolean + + fun refreshFolder(remotePath: String): List } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt new file mode 100644 index 00000000000..ad1c6a4bb3d --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt @@ -0,0 +1,34 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.domain.files + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.files.model.OCFile + +class RefreshFolderFromServerAsyncUseCase( + private val fileRepository: FileRepository +) : BaseUseCaseWithResult, RefreshFolderFromServerAsyncUseCase.Params>() { + override fun run(params: Params): List = + fileRepository.refreshFolder(params.remotePath) + + data class Params( + val remotePath: String + ) + +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt new file mode 100644 index 00000000000..41096491969 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt @@ -0,0 +1,46 @@ +/** + * ownCloud Android client application + * + * @author David González Verdugo + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.files.model + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +//TODO: Add new attributes on demand. Let's try to perform a clean up :) +@Parcelize +data class OCFile( + val id: Long? = null, + val parentId: Long? = null, + val length: Long, + val creationTimestamp: Long, + val modifiedTimestamp: Long, + val remotePath: String, + val mimeType: String, + val etag: String, + val permissions: String, + val remoteId: String, + val privateLink: String +) : Parcelable { + + companion object { + const val PATH_SEPARATOR = "/" + const val ROOT_PATH = PATH_SEPARATOR + } +} From 7576a9e19cfb0a14186ba9d53634df886ee6307c Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 23 Jun 2020 17:52:32 +0200 Subject: [PATCH 2/9] Add first draft to save files in new database --- .../LocalDataSourceModule.kt | 8 +- .../dependecyinjection/RepositoryModule.kt | 2 +- .../SynchronizeFolderOperation.java | 2 - .../32.json | 447 ++++++++++++++++++ .../owncloud/android/data/OwncloudDatabase.kt | 12 +- .../owncloud/android/data/ProviderMeta.java | 3 +- .../files/datasources/LocalFileDataSource.kt | 26 + .../implementation/OCLocalFileDataSource.kt | 36 ++ .../files/datasources/mapper/OCFileMapper.kt | 33 ++ .../datasources/mapper/RemoteFileMapper.kt | 1 + .../owncloud/android/data/files/db/FileDao.kt | 50 ++ .../android/data/files/db/OCFileEntity.kt | 48 ++ .../data/files/repository/OCFileRepository.kt | 8 +- .../android/data/migrations/Migration_32.kt | 29 ++ .../android/domain/files/model/OCFile.kt | 1 + 15 files changed, 696 insertions(+), 10 deletions(-) create mode 100644 owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/32.json create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt index 93613af728c..c3aca696340 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt @@ -27,6 +27,9 @@ import com.owncloud.android.data.authentication.datasources.implementation.OCLoc import com.owncloud.android.data.capabilities.datasources.LocalCapabilitiesDataSource import com.owncloud.android.data.capabilities.datasources.implementation.OCLocalCapabilitiesDataSource import com.owncloud.android.data.capabilities.datasources.mapper.OCCapabilityMapper +import com.owncloud.android.data.files.datasources.LocalFileDataSource +import com.owncloud.android.data.files.datasources.implementation.OCLocalFileDataSource +import com.owncloud.android.data.files.datasources.mapper.OCFileMapper import com.owncloud.android.data.sharing.shares.datasources.LocalShareDataSource import com.owncloud.android.data.sharing.shares.datasources.implementation.OCLocalShareDataSource import com.owncloud.android.data.sharing.shares.datasources.mapper.OCShareMapper @@ -37,18 +40,21 @@ import org.koin.android.ext.koin.androidContext import org.koin.dsl.module val localDataSourceModule = module { - single { AccountManager.get(androidContext())} + single { AccountManager.get(androidContext()) } single { OwncloudDatabase.getDatabase(androidContext()).capabilityDao() } + single { OwncloudDatabase.getDatabase(androidContext()).fileDao() } single { OwncloudDatabase.getDatabase(androidContext()).shareDao() } single { OwncloudDatabase.getDatabase(androidContext()).userDao() } factory { OCCapabilityMapper() } + factory { OCFileMapper() } factory { OCShareMapper() } factory { UserQuotaMapper() } factory { OCLocalAuthenticationDataSource(androidContext(), get(), accountType) } factory { OCLocalCapabilitiesDataSource(get(), get()) } + factory { OCLocalFileDataSource(get(), get()) } factory { OCLocalShareDataSource(get(), get()) } factory { OCLocalUserDataSource(get(), get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt index 57d5f2e166a..1e00bce6c85 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt @@ -38,7 +38,7 @@ import org.koin.dsl.module val repositoryModule = module { factory { OCAuthenticationRepository(get(), get()) } factory { OCCapabilityRepository(get(), get()) } - factory { OCFileRepository(get()) } + factory { OCFileRepository(get(), get()) } factory { OCServerInfoRepository(get()) } factory { OCShareeRepository(get()) } factory { OCShareRepository(get(), get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index 2a8440e08f9..7912533a304 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -33,10 +33,8 @@ import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.resources.files.services.implementation.OCFileService; import com.owncloud.android.operations.common.SyncOperation; diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/32.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/32.json new file mode 100644 index 00000000000..5272f4608fd --- /dev/null +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/32.json @@ -0,0 +1,447 @@ +{ + "formatVersion": 1, + "database": { + "version": 32, + "identityHash": "89b7a96730784521dc5c2fea2bdf9e11", + "entities": [ + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `version_mayor` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMayor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEdition", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "corePollInterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "davChunkingVersion", + "columnName": "dav_chunking_version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filesSharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadOnly", + "columnName": "sharing_public_password_enforced_read_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedReadWrite", + "columnName": "sharing_public_password_enforced_read_write", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicPasswordEnforcedUploadOnly", + "columnName": "sharing_public_password_enforced_public_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filesSharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicMultiple", + "columnName": "sharing_public_multiple", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingPublicSupportsUploadOnly", + "columnName": "supports_upload_only", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesSharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesBigFileChunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "files", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `owner` TEXT NOT NULL, `parentId` INTEGER, `length` INTEGER NOT NULL, `creationTimestamp` INTEGER NOT NULL, `modifiedTimestamp` INTEGER NOT NULL, `remotePath` TEXT NOT NULL, `mimeType` TEXT NOT NULL, `etag` TEXT NOT NULL, `permissions` TEXT NOT NULL, `remoteId` TEXT NOT NULL, `privateLink` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "length", + "columnName": "length", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationTimestamp", + "columnName": "creationTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifiedTimestamp", + "columnName": "modifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remotePath", + "columnName": "remotePath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mimeType", + "columnName": "mimeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateLink", + "columnName": "privateLink", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `file_source` TEXT NOT NULL, `item_source` TEXT NOT NULL, `share_type` INTEGER NOT NULL, `shate_with` TEXT, `path` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `shared_date` INTEGER NOT NULL, `expiration_date` INTEGER NOT NULL, `token` TEXT, `shared_with_display_name` TEXT, `share_with_additional_info` TEXT, `is_directory` INTEGER NOT NULL, `user_id` INTEGER NOT NULL, `id_remote_shared` INTEGER NOT NULL, `owner_share` TEXT NOT NULL, `name` TEXT, `url` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithAdditionalInfo", + "columnName": "share_with_additional_info", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFolder", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "remoteId", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_quotas", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "used", + "columnName": "used", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "accountName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89b7a96730784521dc5c2fea2bdf9e11')" + ] + } +} \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt index 0f3d62bd7eb..dd7b0f91ebf 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt @@ -29,10 +29,13 @@ import androidx.room.RoomDatabase import androidx.room.migration.Migration import com.owncloud.android.data.capabilities.db.OCCapabilityDao import com.owncloud.android.data.capabilities.db.OCCapabilityEntity +import com.owncloud.android.data.files.db.FileDao +import com.owncloud.android.data.files.db.OCFileEntity import com.owncloud.android.data.migrations.MIGRATION_27_28 import com.owncloud.android.data.migrations.MIGRATION_28_29 import com.owncloud.android.data.migrations.MIGRATION_29_30 import com.owncloud.android.data.migrations.MIGRATION_30_31 +import com.owncloud.android.data.migrations.MIGRATION_31_32 import com.owncloud.android.data.sharing.shares.db.OCShareDao import com.owncloud.android.data.sharing.shares.db.OCShareEntity import com.owncloud.android.data.user.db.UserDao @@ -40,16 +43,18 @@ import com.owncloud.android.data.user.db.UserQuotaEntity @Database( entities = [ - OCShareEntity::class, OCCapabilityEntity::class, + OCFileEntity::class, + OCShareEntity::class, UserQuotaEntity::class ], version = ProviderMeta.DB_VERSION, exportSchema = true ) abstract class OwncloudDatabase : RoomDatabase() { - abstract fun shareDao(): OCShareDao abstract fun capabilityDao(): OCCapabilityDao + abstract fun fileDao(): FileDao + abstract fun shareDao(): OCShareDao abstract fun userDao(): UserDao companion object { @@ -60,7 +65,8 @@ abstract class OwncloudDatabase : RoomDatabase() { MIGRATION_27_28, MIGRATION_28_29, MIGRATION_29_30, - MIGRATION_30_31 + MIGRATION_30_31, + MIGRATION_31_32 ) fun getDatabase( diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index ac9c23ee1e1..da85e2445f1 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -31,7 +31,7 @@ public class ProviderMeta { public static final String DB_NAME = "filelist"; public static final String NEW_DB_NAME = "owncloud_database"; - public static final int DB_VERSION = 31; + public static final int DB_VERSION = 32; private ProviderMeta() { } @@ -39,6 +39,7 @@ private ProviderMeta() { static public class ProviderTableMeta implements BaseColumns { public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String CAPABILITIES_TABLE_NAME = "capabilities"; + public static final String OCFILES_TABLE_NAME = "files"; public static final String USER_QUOTAS_TABLE_NAME = "user_quotas"; // Columns of ocshares table diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt new file mode 100644 index 00000000000..1df5c058687 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt @@ -0,0 +1,26 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.data.files.datasources + +import com.owncloud.android.domain.files.model.OCFile + +interface LocalFileDataSource { + fun saveFiles(listOfFiles: List) +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt new file mode 100644 index 00000000000..267a2ede3cb --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -0,0 +1,36 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.data.files.datasources.implementation + +import com.owncloud.android.data.files.datasources.LocalFileDataSource +import com.owncloud.android.data.files.datasources.mapper.OCFileMapper +import com.owncloud.android.data.files.db.FileDao +import com.owncloud.android.domain.files.model.OCFile + +class OCLocalFileDataSource( + private val fileDao: FileDao, + private val ocFileMapper: OCFileMapper +) : LocalFileDataSource { + override fun saveFiles(listOfFiles: List) { + listOfFiles.forEach { + // TODO: Handle conflicts + fileDao.insert(ocFileMapper.toEntity(it)!!) + } + } +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt new file mode 100644 index 00000000000..7d76ecd1844 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt @@ -0,0 +1,33 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.data.files.datasources.mapper + +import com.owncloud.android.data.files.db.OCFileEntity +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.mappers.Mapper + +class OCFileMapper : Mapper { + override fun toModel(entity: OCFileEntity?): OCFile? { + TODO("Not yet implemented") + } + + override fun toEntity(model: OCFile?): OCFileEntity? { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt index b6e840bcb1a..2e20e6e8d0c 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/RemoteFileMapper.kt @@ -26,6 +26,7 @@ class RemoteFileMapper : RemoteMapper { override fun toModel(remote: RemoteFile?): OCFile? = remote?.let { OCFile( + owner = it.owner, remoteId = it.remoteId, remotePath = it.remotePath, length = it.length, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt new file mode 100644 index 00000000000..696b9a304de --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -0,0 +1,50 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.data.files.db + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.owncloud.android.data.ProviderMeta + +@Dao +abstract class FileDao { + + companion object { + private const val SELECT_FILE_WITH_ID = + "SELECT * " + + "FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "WHERE id = :id" + private const val DELETE_FILE_WITH_ID = + "DELETE FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "WHERE id = :id" + } + + @Query(SELECT_FILE_WITH_ID) + abstract fun getFileWithId( + id: Long + ): OCFileEntity? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun insert(ocFileEntity: OCFileEntity) + + @Query(DELETE_FILE_WITH_ID) + abstract fun deleteFileWithId(id: Long) +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt new file mode 100644 index 00000000000..d67408c70fd --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -0,0 +1,48 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.data.files.db + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.owncloud.android.data.ProviderMeta + +@Entity( + tableName = ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME +) +data class OCFileEntity( + @PrimaryKey(autoGenerate = true) + val id: Long, + val owner: String, + val parentId: Long? = null, + val length: Long, + val creationTimestamp: Long, + val modifiedTimestamp: Long, + val remotePath: String, + val mimeType: String, + val etag: String, + val permissions: String, + val remoteId: String, + val privateLink: String +) { + + companion object { + const val PATH_SEPARATOR = "/" + const val ROOT_PATH = PATH_SEPARATOR + } +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 5ebc483a91e..5b0f1fd35a1 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -19,17 +19,21 @@ package com.owncloud.android.data.files.repository +import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.files.model.OCFile class OCFileRepository( - private val remoteFileDataSource: RemoteFileDataSource + private val remoteFileDataSource: RemoteFileDataSource, + private val localFileDataSource: LocalFileDataSource ) : FileRepository { override fun checkPathExistence(path: String, userLogged: Boolean): Boolean = remoteFileDataSource.checkPathExistence(path, userLogged) override fun refreshFolder(remotePath: String): List { - return remoteFileDataSource.refreshFolder(remotePath) + return remoteFileDataSource.refreshFolder(remotePath).also { + localFileDataSource.saveFiles(it) + } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt new file mode 100644 index 00000000000..b2ce786573e --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/migrations/Migration_32.kt @@ -0,0 +1,29 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.data.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +val MIGRATION_31_32 = object : Migration(31, 32) { + override fun migrate(database: SupportSQLiteDatabase) { + //Nothing to migrate at the moment + } +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt index 41096491969..22e53c8e7db 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt @@ -28,6 +28,7 @@ import kotlinx.android.parcel.Parcelize data class OCFile( val id: Long? = null, val parentId: Long? = null, + val owner: String, val length: Long, val creationTimestamp: Long, val modifiedTimestamp: Long, From 56d71b44decf47595c43b38343269fcc2972ea99 Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 24 Jun 2020 09:46:30 +0200 Subject: [PATCH 3/9] Implement ocFile mapper --- .../files/datasources/mapper/OCFileMapper.kt | 39 ++++++++++++++++--- .../android/data/files/db/OCFileEntity.kt | 4 +- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt index 7d76ecd1844..e8f2003c14b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt @@ -23,11 +23,38 @@ import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.mappers.Mapper class OCFileMapper : Mapper { - override fun toModel(entity: OCFileEntity?): OCFile? { - TODO("Not yet implemented") - } + override fun toModel(entity: OCFileEntity?): OCFile? = + entity?.let { + OCFile( + id = it.id, + parentId = it.parentId, + remotePath = it.remotePath, + owner = it.owner, + permissions = it.permissions, + remoteId = it.remoteId, + privateLink = it.privateLink, + creationTimestamp = it.creationTimestamp, + modifiedTimestamp = it.modifiedTimestamp, + etag = it.etag, + mimeType = it.mimeType, + length = it.length + ) + } - override fun toEntity(model: OCFile?): OCFileEntity? { - TODO("Not yet implemented") - } + override fun toEntity(model: OCFile?): OCFileEntity? = + model?.let { + OCFileEntity( + parentId = it.parentId, + remotePath = it.remotePath, + owner = it.owner, + permissions = it.permissions, + remoteId = it.remoteId, + privateLink = it.privateLink, + creationTimestamp = it.creationTimestamp, + modifiedTimestamp = it.modifiedTimestamp, + etag = it.etag, + mimeType = it.mimeType, + length = it.length + ) + } } \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index d67408c70fd..62219651510 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -26,8 +26,6 @@ import com.owncloud.android.data.ProviderMeta tableName = ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME ) data class OCFileEntity( - @PrimaryKey(autoGenerate = true) - val id: Long, val owner: String, val parentId: Long? = null, val length: Long, @@ -40,6 +38,8 @@ data class OCFileEntity( val remoteId: String, val privateLink: String ) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 companion object { const val PATH_SEPARATOR = "/" From 81806aedf18c55781a66d3001214ec1f953cba8b Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 24 Jun 2020 10:50:06 +0200 Subject: [PATCH 4/9] Add parent Id to each file before adding it to database --- .../data/files/datasources/LocalFileDataSource.kt | 2 +- .../implementation/OCLocalFileDataSource.kt | 11 ++++++++--- .../com/owncloud/android/data/files/db/FileDao.kt | 2 +- .../owncloud/android/data/files/db/OCFileEntity.kt | 6 +++--- .../android/data/files/repository/OCFileRepository.kt | 5 ++++- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt index 1df5c058687..de5032f8078 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt @@ -22,5 +22,5 @@ package com.owncloud.android.data.files.datasources import com.owncloud.android.domain.files.model.OCFile interface LocalFileDataSource { - fun saveFiles(listOfFiles: List) + fun saveFilesInFolder(listOfFiles: List, folder: OCFile) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index 267a2ede3cb..85748e7a800 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -27,10 +27,15 @@ class OCLocalFileDataSource( private val fileDao: FileDao, private val ocFileMapper: OCFileMapper ) : LocalFileDataSource { - override fun saveFiles(listOfFiles: List) { + override fun saveFilesInFolder(listOfFiles: List, folder: OCFile) { + // TODO: Handle conflicts + + // Insert first folder container + // TODO: If it is root, add 0 as parent Id + val folderId = fileDao.insert(ocFileMapper.toEntity(folder)!!) listOfFiles.forEach { - // TODO: Handle conflicts - fileDao.insert(ocFileMapper.toEntity(it)!!) + // Add parent id to each file + fileDao.insert(ocFileMapper.toEntity(it)!!.apply { parentId = folderId }) } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index 696b9a304de..b4df0c7cedc 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -43,7 +43,7 @@ abstract class FileDao { ): OCFileEntity? @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun insert(ocFileEntity: OCFileEntity) + abstract fun insert(ocFileEntity: OCFileEntity): Long @Query(DELETE_FILE_WITH_ID) abstract fun deleteFileWithId(id: Long) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index 62219651510..45321228174 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -26,16 +26,16 @@ import com.owncloud.android.data.ProviderMeta tableName = ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME ) data class OCFileEntity( + var parentId: Long? = null, val owner: String, - val parentId: Long? = null, + val remotePath: String, + val remoteId: String, val length: Long, val creationTimestamp: Long, val modifiedTimestamp: Long, - val remotePath: String, val mimeType: String, val etag: String, val permissions: String, - val remoteId: String, val privateLink: String ) { @PrimaryKey(autoGenerate = true) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 5b0f1fd35a1..9f279fa0d63 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -33,7 +33,10 @@ class OCFileRepository( override fun refreshFolder(remotePath: String): List { return remoteFileDataSource.refreshFolder(remotePath).also { - localFileDataSource.saveFiles(it) + localFileDataSource.saveFilesInFolder( + folder = it.first(), + listOfFiles = it.drop(1) + ) } } } From b2507f9b96d8c4a444640c4d8e6783bdc72ee5fa Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 24 Jun 2020 11:30:25 +0200 Subject: [PATCH 5/9] Do not return list of files as result of RefreshFolder use case --- .../android/data/files/repository/OCFileRepository.kt | 4 ++-- .../com/owncloud/android/domain/files/FileRepository.kt | 2 +- .../domain/files/RefreshFolderFromServerAsyncUseCase.kt | 9 +++------ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 9f279fa0d63..c74363d52e8 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -31,8 +31,8 @@ class OCFileRepository( override fun checkPathExistence(path: String, userLogged: Boolean): Boolean = remoteFileDataSource.checkPathExistence(path, userLogged) - override fun refreshFolder(remotePath: String): List { - return remoteFileDataSource.refreshFolder(remotePath).also { + override fun refreshFolder(remotePath: String) { + remoteFileDataSource.refreshFolder(remotePath).also { localFileDataSource.saveFilesInFolder( folder = it.first(), listOfFiles = it.drop(1) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt index a69e71c293a..9121e3fd3bb 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt @@ -24,5 +24,5 @@ import com.owncloud.android.domain.files.model.OCFile interface FileRepository { fun checkPathExistence(path: String, userLogged: Boolean): Boolean - fun refreshFolder(remotePath: String): List + fun refreshFolder(remotePath: String) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt index ad1c6a4bb3d..9f913b6abb4 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/RefreshFolderFromServerAsyncUseCase.kt @@ -23,12 +23,9 @@ import com.owncloud.android.domain.files.model.OCFile class RefreshFolderFromServerAsyncUseCase( private val fileRepository: FileRepository -) : BaseUseCaseWithResult, RefreshFolderFromServerAsyncUseCase.Params>() { - override fun run(params: Params): List = - fileRepository.refreshFolder(params.remotePath) +) : BaseUseCaseWithResult() { + override fun run(params: Params) = fileRepository.refreshFolder(params.remotePath) - data class Params( - val remotePath: String - ) + data class Params(val remotePath: String) } From e7f38d7a5f22df4ab8053a642fa23b805a2ccfe0 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 30 Jun 2020 17:48:43 +0200 Subject: [PATCH 6/9] Merge local and remote files --- .../implementation/OCLocalFileDataSource.kt | 8 ++--- .../owncloud/android/data/files/db/FileDao.kt | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index 85748e7a800..f02c16e6fcc 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -28,14 +28,14 @@ class OCLocalFileDataSource( private val ocFileMapper: OCFileMapper ) : LocalFileDataSource { override fun saveFilesInFolder(listOfFiles: List, folder: OCFile) { - // TODO: Handle conflicts - // Insert first folder container // TODO: If it is root, add 0 as parent Id - val folderId = fileDao.insert(ocFileMapper.toEntity(folder)!!) + val folderId = fileDao.mergeRemoteAndLocalFile(ocFileMapper.toEntity(folder)!!) + + // Then, insert files inside listOfFiles.forEach { // Add parent id to each file - fileDao.insert(ocFileMapper.toEntity(it)!!.apply { parentId = folderId }) + fileDao.mergeRemoteAndLocalFile(ocFileMapper.toEntity(it)!!.apply { parentId = folderId }) } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index b4df0c7cedc..bb081ae0113 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -22,6 +22,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import com.owncloud.android.data.ProviderMeta @Dao @@ -32,6 +33,13 @@ abstract class FileDao { "SELECT * " + "FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + "WHERE id = :id" + + private const val SELECT_FILE_FROM_OWNER_WITH_REMOTE_ID = + "SELECT * " + + "FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "WHERE owner = :owner " + + "AND remoteId = :remoteId" + private const val DELETE_FILE_WITH_ID = "DELETE FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + "WHERE id = :id" @@ -42,9 +50,34 @@ abstract class FileDao { id: Long ): OCFileEntity? + @Query(SELECT_FILE_FROM_OWNER_WITH_REMOTE_ID) + abstract fun getFileFromOwnerAndRemoteId( + owner: String, + remoteId: String + ): OCFileEntity? + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract fun insert(ocFileEntity: OCFileEntity): Long + @Transaction + open fun mergeRemoteAndLocalFile( + ocFileEntity: OCFileEntity + ): Long { + val localFile: OCFileEntity? = getFileFromOwnerAndRemoteId( + owner = ocFileEntity.owner, + remoteId = ocFileEntity.remoteId + ) + if (localFile == null) { + return insert(ocFileEntity) + } else { + // TODO: Handle conflicts, we will replace for the moment + return insert(ocFileEntity.apply { + id = localFile.id + parentId = localFile.parentId + }) + } + } + @Query(DELETE_FILE_WITH_ID) abstract fun deleteFileWithId(id: Long) } From 7277c97f76e51c40996788a27690a21253bda42e Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 30 Jun 2020 18:52:45 +0200 Subject: [PATCH 7/9] Fix and add new tests --- .../datasource/OCRemoteFileDataSourceTest.kt | 5 +- .../file/repository/OCFileRepositoryTest.kt | 59 ++++++++++++++++--- .../com/owncloud/android/testutil/OCFile.kt | 36 +++++++++++ .../owncloud/android/testutil/OCServerInfo.kt | 18 ++++++ 4 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt diff --git a/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt index 03617ec1325..ca7a4a33a32 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt @@ -20,6 +20,8 @@ package com.owncloud.android.data.file.datasource import com.owncloud.android.data.files.datasources.implementation.OCRemoteFileDataSource +import com.owncloud.android.data.files.datasources.mapper.RemoteFileMapper +import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper import com.owncloud.android.lib.resources.files.services.implementation.OCFileService import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.testutil.OC_SERVER_INFO @@ -37,10 +39,11 @@ class OCRemoteFileDataSourceTest { private lateinit var ocRemoteFileDataSource: OCRemoteFileDataSource private val ocFileService: OCFileService = mockk() + private val remoteFileMapper = RemoteFileMapper() @Before fun init() { - ocRemoteFileDataSource = OCRemoteFileDataSource(ocFileService) + ocRemoteFileDataSource = OCRemoteFileDataSource(ocFileService, remoteFileMapper) } @Test diff --git a/owncloudData/src/test/java/com/owncloud/android/data/file/repository/OCFileRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/file/repository/OCFileRepositoryTest.kt index ae2af1cfc05..70b67b6aaf3 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/file/repository/OCFileRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/file/repository/OCFileRepositoryTest.kt @@ -19,24 +19,29 @@ package com.owncloud.android.data.file.repository -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.owncloud.android.data.files.repository.OCFileRepository +import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.datasources.RemoteFileDataSource +import com.owncloud.android.data.files.repository.OCFileRepository import com.owncloud.android.domain.exceptions.NoConnectionWithServerException +import com.owncloud.android.testutil.OC_FILE import com.owncloud.android.testutil.OC_SERVER_INFO import io.mockk.every import io.mockk.mockk import io.mockk.verify -import org.junit.Rule import org.junit.Test class OCFileRepositoryTest { - @Rule - @JvmField - val instantExecutorRule = InstantTaskExecutorRule() private val remoteFileDataSource = mockk(relaxed = true) - private val ocFileRepository: OCFileRepository = OCFileRepository(remoteFileDataSource) + private val localFileDataSource = mockk(relaxed = true) + private val ocFileRepository: OCFileRepository = OCFileRepository(remoteFileDataSource, localFileDataSource) + + private val folderToFetch = OC_FILE + private val listOfFilesRetrieved = listOf( + folderToFetch, + OC_FILE.copy(remoteId = "one"), + OC_FILE.copy(remoteId = "two") + ) @Test fun checkPathExistenceExists() { @@ -51,7 +56,12 @@ class OCFileRepositoryTest { @Test(expected = NoConnectionWithServerException::class) fun checkPathExistenceExistsNoConnection() { - every { remoteFileDataSource.checkPathExistence(OC_SERVER_INFO.baseUrl, false) } throws NoConnectionWithServerException() + every { + remoteFileDataSource.checkPathExistence( + OC_SERVER_INFO.baseUrl, + false + ) + } throws NoConnectionWithServerException() ocFileRepository.checkPathExistence(OC_SERVER_INFO.baseUrl, false) @@ -59,4 +69,37 @@ class OCFileRepositoryTest { remoteFileDataSource.checkPathExistence(OC_SERVER_INFO.baseUrl, false) } } + + @Test + fun refreshFolderOk() { + every { + remoteFileDataSource.refreshFolder(folderToFetch.remotePath) + } returns listOfFilesRetrieved + + ocFileRepository.refreshFolder(folderToFetch.remotePath) + + verify(exactly = 1) { + remoteFileDataSource.refreshFolder(folderToFetch.remotePath) + localFileDataSource.saveFilesInFolder( + listOfFiles = listOfFilesRetrieved.drop(1), + folder = listOfFilesRetrieved.first() + ) + } + } + + @Test(expected = NoConnectionWithServerException::class) + fun refreshFolderNoConnection() { + every { + remoteFileDataSource.refreshFolder(folderToFetch.remotePath) + } throws NoConnectionWithServerException() + + ocFileRepository.refreshFolder(folderToFetch.remotePath) + + verify(exactly = 1) { + remoteFileDataSource.refreshFolder(OC_FILE.remotePath) + } + verify(exactly = 0) { + localFileDataSource.saveFilesInFolder(any(), any()) + } + } } diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt new file mode 100644 index 00000000000..815e50e237c --- /dev/null +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -0,0 +1,36 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.testutil + +import com.owncloud.android.domain.files.model.OCFile + +val OC_FILE = OCFile( + id = 122, + parentId = 123, + remotePath = "/Photos", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay", + privateLink = "http://server.url/f/3", + creationTimestamp = 0, + modifiedTimestamp = 1593510589000, + etag = "5efb0c13c688f", + mimeType = "DIR", + length = 123123123 +) diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCServerInfo.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCServerInfo.kt index 4a615bca124..2f33de6ce21 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCServerInfo.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCServerInfo.kt @@ -1,3 +1,21 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package com.owncloud.android.testutil import com.owncloud.android.domain.server.model.AuthenticationMethod From cbb9468250b4d6d474678045510676a8cb823488 Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 8 Jul 2020 14:38:57 +0200 Subject: [PATCH 8/9] Add usecase test --- .../files/datasources/mapper/OCFileMapper.kt | 26 ++--- .../datasource/OCRemoteFileDataSourceTest.kt | 1 - .../android/domain/files/model/OCFileTest.kt | 99 +++++++++++++++++++ ...RefreshFolderFromServerAsyncUseCaseTest.kt | 60 +++++++++++ 4 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt create mode 100644 owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/RefreshFolderFromServerAsyncUseCaseTest.kt diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt index e8f2003c14b..b8be407c2cf 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/mapper/OCFileMapper.kt @@ -43,18 +43,18 @@ class OCFileMapper : Mapper { override fun toEntity(model: OCFile?): OCFileEntity? = model?.let { - OCFileEntity( - parentId = it.parentId, - remotePath = it.remotePath, - owner = it.owner, - permissions = it.permissions, - remoteId = it.remoteId, - privateLink = it.privateLink, - creationTimestamp = it.creationTimestamp, - modifiedTimestamp = it.modifiedTimestamp, - etag = it.etag, - mimeType = it.mimeType, - length = it.length - ) + OCFileEntity( + parentId = it.parentId, + remotePath = it.remotePath, + owner = it.owner, + permissions = it.permissions, + remoteId = it.remoteId, + privateLink = it.privateLink, + creationTimestamp = it.creationTimestamp, + modifiedTimestamp = it.modifiedTimestamp, + etag = it.etag, + mimeType = it.mimeType, + length = it.length + ) } } \ No newline at end of file diff --git a/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt index ca7a4a33a32..027a4641b7d 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/file/datasource/OCRemoteFileDataSourceTest.kt @@ -21,7 +21,6 @@ package com.owncloud.android.data.file.datasource import com.owncloud.android.data.files.datasources.implementation.OCRemoteFileDataSource import com.owncloud.android.data.files.datasources.mapper.RemoteFileMapper -import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper import com.owncloud.android.lib.resources.files.services.implementation.OCFileService import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.testutil.OC_SERVER_INFO diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt new file mode 100644 index 00000000000..a9e8f9da17d --- /dev/null +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt @@ -0,0 +1,99 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.domain.files.model + +import com.owncloud.android.testutil.OC_ACCOUNT_NAME +import org.junit.Assert +import org.junit.Test + +class OCFileTest { + + @Test + fun testEqualsOk() { + val item1 = OCFile( + 122, + 123, + OC_ACCOUNT_NAME, + 123123123, + 0, + 1593510589000, + "/Photos", + "DIR", + "5efb0c13c688f", + "RDNVCK", + "00000003oci9p7er2hay", + "http://server.url/f/3" + ) + + val item2 = OCFile( + id = 122, + parentId = 123, + remotePath = "/Photos", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay", + privateLink = "http://server.url/f/3", + creationTimestamp = 0, + modifiedTimestamp = 1593510589000, + etag = "5efb0c13c688f", + mimeType = "DIR", + length = 123123123 + ) + + + Assert.assertTrue(item1 == item2) + Assert.assertFalse(item1 === item2) + } + + @Test + fun testEqualsKo() { + val item1 = OCFile( + 123, + 122, + OC_ACCOUNT_NAME, + 123123123, + 0, + 1593510589000, + "/Photos", + "DIR", + "5efb0c13c688f", + "RDNVCK", + "00000003oci9p7er2hay", + "http://server.url/f/3" + ) + + val item2 = OCFile( + id = 122, + parentId = 123, + remotePath = "/Photos", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay", + privateLink = "http://server.url/f/3", + creationTimestamp = 0, + modifiedTimestamp = 1593510589000, + etag = "5efb0c13c688f", + mimeType = "DIR", + length = 123123123 + ) + + Assert.assertFalse(item1 == item2) + Assert.assertFalse(item1 === item2) + } +} diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/RefreshFolderFromServerAsyncUseCaseTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/RefreshFolderFromServerAsyncUseCaseTest.kt new file mode 100644 index 00000000000..57d3e28d76c --- /dev/null +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/RefreshFolderFromServerAsyncUseCaseTest.kt @@ -0,0 +1,60 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.domain.files.usecases + +import com.owncloud.android.domain.exceptions.UnauthorizedException +import com.owncloud.android.domain.files.FileRepository +import com.owncloud.android.domain.files.RefreshFolderFromServerAsyncUseCase +import io.mockk.every +import io.mockk.spyk +import io.mockk.verify +import org.junit.Assert +import org.junit.Test + +class RefreshFolderFromServerAsyncUseCaseTest { + private val fileRepository: FileRepository = spyk() + private val useCase = RefreshFolderFromServerAsyncUseCase(fileRepository) + private val useCaseParams = RefreshFolderFromServerAsyncUseCase.Params("/Photos") + + @Test + fun refreshFolderFromServerOk() { + val useCaseResult = useCase.execute(useCaseParams) + + Assert.assertTrue(useCaseResult.isSuccess) + Assert.assertFalse(useCaseResult.isError) + Assert.assertEquals(Unit, useCaseResult.getDataOrNull()) + + verify(exactly = 1) { fileRepository.refreshFolder(useCaseParams.remotePath) } + } + + @Test + fun refreshCapabilitiesFromServerWithUnauthorizedException() { + every { fileRepository.refreshFolder(any()) } throws UnauthorizedException() + + val useCaseResult = useCase.execute(useCaseParams) + + Assert.assertFalse(useCaseResult.isSuccess) + Assert.assertTrue(useCaseResult.isError) + + Assert.assertNull(useCaseResult.getDataOrNull()) + Assert.assertTrue(useCaseResult.getThrowableOrNull() is UnauthorizedException) + + verify(exactly = 1) { fileRepository.refreshFolder(useCaseParams.remotePath) } + } +} \ No newline at end of file From 73414b868818d3c29353e0f40af2238404fd24ee Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 28 Jul 2020 13:56:43 +0200 Subject: [PATCH 9/9] Apply CR suggestions --- owncloud-android-library | 2 +- .../owncloud/android/data/ProviderMeta.java | 2 +- .../owncloud/android/data/files/db/FileDao.kt | 6 ++--- .../android/data/files/db/OCFileEntity.kt | 2 +- .../com/owncloud/android/testutil/OCFile.kt | 24 +++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/owncloud-android-library b/owncloud-android-library index afb064c5dcb..ea1f4ec33ee 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit afb064c5dcbbd71cab38fa1aceb2e0010ad2683b +Subproject commit ea1f4ec33ee9531dee4c462d035a2ca0da01d112 diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index da85e2445f1..aae0f145388 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -39,7 +39,7 @@ private ProviderMeta() { static public class ProviderTableMeta implements BaseColumns { public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String CAPABILITIES_TABLE_NAME = "capabilities"; - public static final String OCFILES_TABLE_NAME = "files"; + public static final String FILES_TABLE_NAME = "files"; public static final String USER_QUOTAS_TABLE_NAME = "user_quotas"; // Columns of ocshares table diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index bb081ae0113..b2bbce7be82 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -31,17 +31,17 @@ abstract class FileDao { companion object { private const val SELECT_FILE_WITH_ID = "SELECT * " + - "FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "WHERE id = :id" private const val SELECT_FILE_FROM_OWNER_WITH_REMOTE_ID = "SELECT * " + - "FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "WHERE owner = :owner " + "AND remoteId = :remoteId" private const val DELETE_FILE_WITH_ID = - "DELETE FROM ${ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME} " + + "DELETE FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "WHERE id = :id" } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index 45321228174..ad0b3331fcc 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -23,7 +23,7 @@ import androidx.room.PrimaryKey import com.owncloud.android.data.ProviderMeta @Entity( - tableName = ProviderMeta.ProviderTableMeta.OCFILES_TABLE_NAME + tableName = ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME ) data class OCFileEntity( var parentId: Long? = null, diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt index 815e50e237c..8d4cb51a812 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -21,16 +21,16 @@ package com.owncloud.android.testutil import com.owncloud.android.domain.files.model.OCFile val OC_FILE = OCFile( - id = 122, - parentId = 123, - remotePath = "/Photos", - owner = OC_ACCOUNT_NAME, - permissions = "RDNVCK", - remoteId = "00000003oci9p7er2hay", - privateLink = "http://server.url/f/3", - creationTimestamp = 0, - modifiedTimestamp = 1593510589000, - etag = "5efb0c13c688f", - mimeType = "DIR", - length = 123123123 + id = 122, + parentId = 123, + remotePath = "/Photos", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay", + privateLink = "http://server.url/f/3", + creationTimestamp = 0, + modifiedTimestamp = 1593510589000, + etag = "5efb0c13c688f", + mimeType = "DIR", + length = 123123123 )