From ff5596d0631e93746494c017797d0191b6bdb0b1 Mon Sep 17 00:00:00 2001 From: Ariel Gentile Date: Fri, 17 Feb 2023 23:10:09 -0300 Subject: [PATCH] feat!: add data, cache and temp dirs to FileSystem (#1306) Signed-off-by: Ariel Gentile BREAKING CHANGE: Agent-produced files will now be divided in different system paths depending on their nature: data, temp and cache. Previously, they were located at a single location, defaulting to a temporary directory. If you specified a custom path in `FileSystem` object constructor, you now must provide an object containing `baseDataPath`, `baseTempPath` and `baseCachePath`. They can point to the same path, although it's recommended to specify different path to avoid future file clashes. --- packages/anoncreds/src/utils/tails.ts | 6 ++-- packages/askar/src/utils/askarWalletConfig.ts | 13 ++++++-- packages/askar/src/wallet/AskarWallet.ts | 4 +-- .../indy/services/IndyUtilitiesService.ts | 2 +- packages/core/src/modules/ledger/IndyPool.ts | 2 +- packages/core/src/storage/FileSystem.ts | 5 ++- .../src/storage/migration/UpdateAssistant.ts | 7 +++- .../storage/migration/__tests__/0.1.test.ts | 24 -------------- .../storage/migration/__tests__/0.2.test.ts | 19 ----------- .../storage/migration/__tests__/0.3.test.ts | 4 --- .../migration/__tests__/backup.test.ts | 15 ++++++--- packages/indy-sdk/src/ledger/IndySdkPool.ts | 2 +- packages/node/src/NodeFileSystem.ts | 33 +++++++++++++------ packages/node/tests/NodeFileSystem.test.ts | 2 +- packages/react-native/package.json | 4 +-- .../react-native/src/ReactNativeFileSystem.ts | 30 ++++++++++++++--- 16 files changed, 91 insertions(+), 81 deletions(-) diff --git a/packages/anoncreds/src/utils/tails.ts b/packages/anoncreds/src/utils/tails.ts index 9ae29aa8e4..e706f00914 100644 --- a/packages/anoncreds/src/utils/tails.ts +++ b/packages/anoncreds/src/utils/tails.ts @@ -2,11 +2,11 @@ import type { AgentContext, FileSystem } from '@aries-framework/core' import { TypedArrayEncoder, InjectionSymbols } from '@aries-framework/core' -const getTailsFilePath = (basePath: string, tailsHash: string) => `${basePath}/afj/anoncreds/tails/${tailsHash}` +const getTailsFilePath = (cachePath: string, tailsHash: string) => `${cachePath}/anoncreds/tails/${tailsHash}` export function tailsFileExists(agentContext: AgentContext, tailsHash: string): Promise { const fileSystem = agentContext.dependencyManager.resolve(InjectionSymbols.FileSystem) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHash) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHash) return fileSystem.exists(tailsFilePath) } @@ -27,7 +27,7 @@ export async function downloadTailsFile( // hash is used as file identifier const tailsExists = await tailsFileExists(agentContext, tailsHashBase58) - const tailsFilePath = getTailsFilePath(fileSystem.basePath, tailsHashBase58) + const tailsFilePath = getTailsFilePath(fileSystem.cachePath, tailsHashBase58) agentContext.config.logger.debug( `Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${tailsFilePath}` ) diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 2337988f26..b6e885ebe5 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -18,7 +18,16 @@ export const keyDerivationMethodToStoreKeyMethod = (keyDerivationMethod?: KeyDer return correspondenceTable[keyDerivationMethod] as StoreKeyMethod } -export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string): { uri: string; path?: string } => { +/** + * Creates a proper askar wallet URI value based on walletConfig + * @param walletConfig WalletConfig object + * @param afjDataPath framework data path (used in case walletConfig.storage.path is undefined) + * @returns string containing the askar wallet URI + */ +export const uriFromWalletConfig = ( + walletConfig: WalletConfig, + afjDataPath: string +): { uri: string; path?: string } => { let uri = '' let path @@ -31,7 +40,7 @@ export const uriFromWalletConfig = (walletConfig: WalletConfig, basePath: string if (walletConfig.storage.inMemory) { uri = 'sqlite://:memory:' } else { - path = `${(walletConfig.storage.path as string) ?? basePath + '/wallet'}/${walletConfig.id}/sqlite.db` + path = (walletConfig.storage.path as string) ?? `${afjDataPath}/wallet/${walletConfig.id}/sqlite.db` uri = `sqlite://${path}` } } else if (walletConfig.storage.type === 'postgres') { diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index baa17838a4..38030c6ab7 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -289,7 +289,7 @@ export class AskarWallet implements Wallet { } try { - const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.basePath) + const { uri } = uriFromWalletConfig(this.walletConfig, this.fileSystem.dataPath) await Store.remove(uri) } catch (error) { const errorMessage = `Error deleting wallet '${this.walletConfig.id}': ${error.message}` @@ -689,7 +689,7 @@ export class AskarWallet implements Wallet { } private async getAskarWalletConfig(walletConfig: WalletConfig) { - const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.basePath) + const { uri, path } = uriFromWalletConfig(walletConfig, this.fileSystem.dataPath) // Make sure path exists before creating the wallet if (path) { diff --git a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts index eef01ccfd2..44b9713352 100644 --- a/packages/core/src/modules/indy/services/IndyUtilitiesService.ts +++ b/packages/core/src/modules/indy/services/IndyUtilitiesService.ts @@ -63,7 +63,7 @@ export class IndyUtilitiesService { public async downloadTails(hash: string, tailsLocation: string): Promise { try { this.logger.debug(`Checking to see if tails file for URL ${tailsLocation} has been stored in the FileSystem`) - const filePath = `${this.fileSystem.basePath}/afj/tails/${hash}` + const filePath = `${this.fileSystem.cachePath}/tails/${hash}` const tailsExists = await this.fileSystem.exists(filePath) this.logger.debug(`Tails file for ${tailsLocation} ${tailsExists ? 'is stored' : 'is not stored'} at ${filePath}`) diff --git a/packages/core/src/modules/ledger/IndyPool.ts b/packages/core/src/modules/ledger/IndyPool.ts index c3d2249799..d8abffc113 100644 --- a/packages/core/src/modules/ledger/IndyPool.ts +++ b/packages/core/src/modules/ledger/IndyPool.ts @@ -181,7 +181,7 @@ export class IndyPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/core/src/storage/FileSystem.ts b/packages/core/src/storage/FileSystem.ts index c5996e78b2..a6eeb08e48 100644 --- a/packages/core/src/storage/FileSystem.ts +++ b/packages/core/src/storage/FileSystem.ts @@ -5,11 +5,14 @@ export interface DownloadToFileOptions { } export interface FileSystem { - readonly basePath: string + readonly dataPath: string + readonly cachePath: string + readonly tempPath: string exists(path: string): Promise createDirectory(path: string): Promise write(path: string, data: string): Promise read(path: string): Promise + delete(path: string): Promise downloadToFile(url: string, path: string, options?: DownloadToFileOptions): Promise } diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index f4647d4891..3c08c2fe64 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -157,6 +157,8 @@ export class UpdateAssistant = BaseAgent> { `Successfully updated agent storage from version ${update.fromVersion} to version ${update.toVersion}` ) } + // Delete backup file, as it is not needed anymore + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) } catch (error) { this.agent.config.logger.fatal('An error occurred while updating the wallet. Restoring backup', { error, @@ -164,6 +166,9 @@ export class UpdateAssistant = BaseAgent> { // In the case of an error we want to restore the backup await this.restoreBackup(updateIdentifier) + // Delete backup file, as wallet was already restored (backup-error file will persist though) + await this.fileSystem.delete(this.getBackupPath(updateIdentifier)) + throw error } } catch (error) { @@ -192,7 +197,7 @@ export class UpdateAssistant = BaseAgent> { } private getBackupPath(backupIdentifier: string) { - return `${this.fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + return `${this.fileSystem.dataPath}/migration/backup/${backupIdentifier}` } private async createBackup(backupIdentifier: string) { diff --git a/packages/core/src/storage/migration/__tests__/0.1.test.ts b/packages/core/src/storage/migration/__tests__/0.1.test.ts index ad2dd0b837..f38ba94a87 100644 --- a/packages/core/src/storage/migration/__tests__/0.1.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.1.test.ts @@ -48,8 +48,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy, @@ -79,10 +77,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot(mediationRoleUpdateStrategy) - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() } @@ -110,8 +104,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -142,10 +134,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -174,8 +162,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -206,10 +192,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -242,8 +224,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -274,10 +254,6 @@ describe('UpdateAssistant | v0.1 - v0.2', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.2.test.ts b/packages/core/src/storage/migration/__tests__/0.2.test.ts index 08ed9dce64..9fb991253b 100644 --- a/packages/core/src/storage/migration/__tests__/0.2.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.2.test.ts @@ -13,7 +13,6 @@ import { UpdateAssistant } from '../UpdateAssistant' const backupDate = new Date('2022-01-21T22:50:20.522Z') jest.useFakeTimers().setSystemTime(backupDate) -const backupIdentifier = backupDate.getTime() const walletConfig = { id: `Wallet: 0.2 Update`, @@ -46,8 +45,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - const updateAssistant = new UpdateAssistant(agent, { v0_1ToV0_2: { mediationRoleUpdateStrategy: 'doNotChange', @@ -83,10 +80,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -119,8 +112,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -137,10 +128,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() @@ -170,8 +157,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { dependencyManager ) - const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - // We need to manually initialize the wallet as we're using the in memory wallet service // When we call agent.initialize() it will create the wallet and store the current framework // version in the in memory storage service. We need to manually set the records between initializing @@ -189,10 +174,6 @@ describe('UpdateAssistant | v0.2 - v0.3.1', () => { expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/0.3.test.ts b/packages/core/src/storage/migration/__tests__/0.3.test.ts index b797fc7c97..124cdf0a88 100644 --- a/packages/core/src/storage/migration/__tests__/0.3.test.ts +++ b/packages/core/src/storage/migration/__tests__/0.3.test.ts @@ -75,10 +75,6 @@ describe('UpdateAssistant | v0.3 - v0.3.1', () => { delete storageService.records.MEDIATOR_ROUTING_RECORD expect(storageService.records).toMatchSnapshot() - // Need to remove backupFiles after each run so we don't get IOErrors - const backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` - unlinkSync(backupPath) - await agent.shutdown() await agent.wallet.delete() diff --git a/packages/core/src/storage/migration/__tests__/backup.test.ts b/packages/core/src/storage/migration/__tests__/backup.test.ts index 73aba5823f..8a8b351a38 100644 --- a/packages/core/src/storage/migration/__tests__/backup.test.ts +++ b/packages/core/src/storage/migration/__tests__/backup.test.ts @@ -32,7 +32,7 @@ describe('UpdateAssistant | Backup', () => { beforeEach(async () => { agent = new Agent(agentOptions) const fileSystem = agent.dependencyManager.resolve(InjectionSymbols.FileSystem) - backupPath = `${fileSystem.basePath}/afj/migration/backup/${backupIdentifier}` + backupPath = `${fileSystem.dataPath}/migration/backup/${backupIdentifier}` // If tests fail it's possible the cleanup has been skipped. So remove before running tests const doesFileSystemExist = await fileSystem.exists(backupPath) @@ -85,11 +85,16 @@ describe('UpdateAssistant | Backup', () => { // Backup should not exist before update expect(await fileSystem.exists(backupPath)).toBe(false) + const walletSpy = jest.spyOn(agent.wallet, 'export') + // Create update await updateAssistant.update() - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // A wallet export should have been initiated + expect(walletSpy).toHaveBeenCalledWith({ key: agent.wallet.walletConfig?.key, path: backupPath }) + + // Backup should be cleaned after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect( (await credentialRepository.getAll(agent.context)).sort((a, b) => a.id.localeCompare(b.id)) @@ -142,8 +147,8 @@ describe('UpdateAssistant | Backup', () => { expect(updateError?.cause?.message).toEqual("Uh oh I'm broken") - // Backup should exist after update - expect(await fileSystem.exists(backupPath)).toBe(true) + // Only backup error should exist after update + expect(await fileSystem.exists(backupPath)).toBe(false) expect(await fileSystem.exists(`${backupPath}-error`)).toBe(true) // Wallet should be same as when we started because of backup diff --git a/packages/indy-sdk/src/ledger/IndySdkPool.ts b/packages/indy-sdk/src/ledger/IndySdkPool.ts index bae9d8e17b..dfa25feb37 100644 --- a/packages/indy-sdk/src/ledger/IndySdkPool.ts +++ b/packages/indy-sdk/src/ledger/IndySdkPool.ts @@ -180,7 +180,7 @@ export class IndySdkPool { if (this.poolConfig.genesisPath) return this.poolConfig.genesisPath // Determine the genesisPath - const genesisPath = this.fileSystem.basePath + `/afj/genesis-${this.poolConfig.id}.txn` + const genesisPath = this.fileSystem.tempPath + `/genesis-${this.poolConfig.id}.txn` // Store genesis data if provided if (this.poolConfig.genesisTransactions) { await this.fileSystem.write(genesisPath, this.poolConfig.genesisTransactions) diff --git a/packages/node/src/NodeFileSystem.ts b/packages/node/src/NodeFileSystem.ts index a5caf0d070..75ebb01b73 100644 --- a/packages/node/src/NodeFileSystem.ts +++ b/packages/node/src/NodeFileSystem.ts @@ -5,21 +5,30 @@ import { createHash } from 'crypto' import fs, { promises } from 'fs' import http from 'http' import https from 'https' -import { tmpdir } from 'os' +import { tmpdir, homedir } from 'os' import { dirname } from 'path' -const { access, readFile, writeFile } = promises +const { access, readFile, writeFile, mkdir, rm, unlink } = promises export class NodeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new NodeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. process.cwd() if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to homedir() + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to homedir() + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to tmpdir() */ - public constructor(basePath?: string) { - this.basePath = basePath ?? tmpdir() + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = options?.baseDataPath ? `${options?.baseDataPath}/.afj` : `${homedir()}/.afj/data` + this.cachePath = options?.baseCachePath ? `${options?.baseCachePath}/.afj` : `${homedir()}/.afj/cache` + this.tempPath = `${options?.baseTempPath ?? tmpdir()}/.afj` } public async exists(path: string) { @@ -32,12 +41,12 @@ export class NodeFileSystem implements FileSystem { } public async createDirectory(path: string): Promise { - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) } public async write(path: string, data: string): Promise { // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) return writeFile(path, data, { encoding: 'utf-8' }) } @@ -46,11 +55,15 @@ export class NodeFileSystem implements FileSystem { return readFile(path, { encoding: 'utf-8' }) } + public async delete(path: string): Promise { + await rm(path, { recursive: true, force: true }) + } + public async downloadToFile(url: string, path: string, options: DownloadToFileOptions) { const httpMethod = url.startsWith('https') ? https : http // Make sure parent directories exist - await promises.mkdir(dirname(path), { recursive: true }) + await mkdir(dirname(path), { recursive: true }) const file = fs.createWriteStream(path) const hash = options.verifyHash ? createHash('sha256') : undefined @@ -88,7 +101,7 @@ export class NodeFileSystem implements FileSystem { }) .on('error', async (error) => { // Handle errors - await fs.promises.unlink(path) // Delete the file async. (But we don't check the result) + await unlink(path) // Delete the file async. (But we don't check the result) reject(`Unable to download file from url: ${url}. ${error.message}`) }) }) diff --git a/packages/node/tests/NodeFileSystem.test.ts b/packages/node/tests/NodeFileSystem.test.ts index f031ee32e5..f62f4d8e00 100644 --- a/packages/node/tests/NodeFileSystem.test.ts +++ b/packages/node/tests/NodeFileSystem.test.ts @@ -28,7 +28,7 @@ describe('@aries-framework/file-system-node', () => { await fileSystem.downloadToFile( 'https://tails.prod.absa.africa/api/public/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p', - `${fileSystem.basePath}/afj/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, + `${fileSystem.dataPath}/tails/4B1NxYuGxwYMe5BAyP9NXkUmbEkDATo4oGZCgjXQ3y1p`, { verifyHash: { algorithm: 'sha256', diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 3fa303bf91..1da533811d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -26,11 +26,11 @@ "dependencies": { "@aries-framework/core": "0.3.3", "@azure/core-asynciterator-polyfill": "^1.0.0", - "events": "^3.3.0" + "events": "^3.3.0", + "@types/react-native": "^0.64.10" }, "devDependencies": { "@types/indy-sdk-react-native": "npm:@types/indy-sdk@1.16.26", - "@types/react-native": "^0.64.10", "indy-sdk-react-native": "^0.3.1", "react": "17.0.1", "react-native": "0.64.2", diff --git a/packages/react-native/src/ReactNativeFileSystem.ts b/packages/react-native/src/ReactNativeFileSystem.ts index bf8e9fb353..48588fad88 100644 --- a/packages/react-native/src/ReactNativeFileSystem.ts +++ b/packages/react-native/src/ReactNativeFileSystem.ts @@ -1,20 +1,38 @@ import type { FileSystem, DownloadToFileOptions } from '@aries-framework/core' import { TypedArrayEncoder, AriesFrameworkError, getDirFromFilePath, Buffer } from '@aries-framework/core' +import { Platform } from 'react-native' import * as RNFS from 'react-native-fs' export class ReactNativeFileSystem implements FileSystem { - public readonly basePath + public readonly dataPath + public readonly cachePath + public readonly tempPath /** * Create new ReactNativeFileSystem class instance. * - * @param basePath The base path to use for reading and writing files. RNFS.TemporaryDirectoryPath if not specified + * @param baseDataPath The base path to use for reading and writing data files used within the framework. + * Files will be created under baseDataPath/.afj directory. If not specified, it will be set to + * RNFS.DocumentDirectoryPath + * @param baseCachePath The base path to use for reading and writing cache files used within the framework. + * Files will be created under baseCachePath/.afj directory. If not specified, it will be set to + * RNFS.CachesDirectoryPath + * @param baseTempPath The base path to use for reading and writing temporary files within the framework. + * Files will be created under baseTempPath/.afj directory. If not specified, it will be set to + * RNFS.TemporaryDirectoryPath * * @see https://github.com/itinance/react-native-fs#constants */ - public constructor(basePath?: string) { - this.basePath = basePath ?? RNFS.TemporaryDirectoryPath + public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { + this.dataPath = `${options?.baseDataPath ?? RNFS.DocumentDirectoryPath}/.afj` + // In Android, TemporaryDirectoryPath falls back to CachesDirectoryPath + this.cachePath = options?.baseCachePath + ? `${options?.baseCachePath}/.afj` + : `${RNFS.CachesDirectoryPath}/.afj${Platform.OS === 'android' ? '/cache' : ''}` + this.tempPath = options?.baseTempPath + ? `${options?.baseTempPath}/.afj` + : `${RNFS.TemporaryDirectoryPath}/.afj${Platform.OS === 'android' ? '/temp' : ''}` } public async exists(path: string): Promise { @@ -36,6 +54,10 @@ export class ReactNativeFileSystem implements FileSystem { return RNFS.readFile(path, 'utf8') } + public async delete(path: string): Promise { + await RNFS.unlink(path) + } + public async downloadToFile(url: string, path: string, options?: DownloadToFileOptions) { // Make sure parent directories exist await RNFS.mkdir(getDirFromFilePath(path))