-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor!: migrate from .afj to .credo #2161
Changes from all commits
57fd858
cb853f6
83bc5d2
decddb4
b3c1f86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { BaseAgent } from '../../../../agent/BaseAgent' | ||
|
||
import { migrateToCredoFolder } from './migrateToCredoFolder' | ||
|
||
export async function updateV0_5ToV0_6<Agent extends BaseAgent>(agent: Agent): Promise<void> { | ||
await migrateToCredoFolder(agent) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type { BaseAgent } from '../../../../agent/BaseAgent' | ||
import type { FileSystem } from '../../../FileSystem' | ||
|
||
import { InjectionSymbols } from '../../../../constants' | ||
import { CredoError } from '../../../../error' | ||
|
||
/** | ||
* Migrates the sqlite folder location from .afj to .credo in the storage directory in node and react native. | ||
* | ||
*/ | ||
export async function migrateToCredoFolder<Agent extends BaseAgent>(agent: Agent) { | ||
const walletId = agent.config.walletConfig?.id | ||
|
||
if (!walletId) { | ||
throw new CredoError('Wallet id is required to migrate the wallet to .credo') | ||
} | ||
|
||
// Adding type assertion to get the storage config | ||
const storageConfig = agent.config.walletConfig?.storage as { | ||
config?: { inMemory?: boolean } | ||
} | ||
|
||
// If no storage config is provided, we set default as sqlite | ||
// https://github.com/openwallet-foundation/credo-ts/blob/main/packages/askar/src/utils/askarWalletConfig.ts#L35 | ||
// and we only migrate the data folder if the storage config is not set to inMemory | ||
if (!storageConfig || (storageConfig.config && !storageConfig.config?.inMemory)) { | ||
return | ||
} | ||
|
||
agent.config.logger.info('Migrating data from .afj to .credo') | ||
|
||
const fileSystem = agent.dependencyManager.resolve<FileSystem>(InjectionSymbols.FileSystem) | ||
|
||
await fileSystem.migrateWalletToCredoFolder(walletId) | ||
agent.config.logger.info('Migration completed successfully') | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ import https from 'https' | |
import { tmpdir, homedir } from 'os' | ||
import { dirname } from 'path' | ||
|
||
const { access, readFile, writeFile, mkdir, rm, unlink, copyFile } = promises | ||
const { access, readFile, writeFile, mkdir, rm, unlink, copyFile, cp } = promises | ||
|
||
export class NodeFileSystem implements FileSystem { | ||
public readonly dataPath | ||
|
@@ -19,16 +19,61 @@ export class NodeFileSystem implements FileSystem { | |
* Create new NodeFileSystem class instance. | ||
* | ||
* @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() | ||
* Files will be created under baseDataPath/.credo 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() | ||
* Files will be created under baseCachePath/.credo 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() | ||
* Files will be created under baseTempPath/.credo directory. If not specified, it will be set to 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` | ||
this.dataPath = options?.baseDataPath ? `${options?.baseDataPath}/.credo` : `${homedir()}/.credo/data` | ||
this.cachePath = options?.baseCachePath ? `${options?.baseCachePath}/.credo` : `${homedir()}/.credo/cache` | ||
this.tempPath = `${options?.baseTempPath ?? tmpdir()}/.credo` | ||
} | ||
|
||
/** | ||
* Migrate data from .afj to .credo if .afj exists. | ||
* Copy the contents from old directory (.afj) to new directory (.credo). | ||
*/ | ||
public async migrateWalletToCredoFolder(walletId: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the file system should stay very generic. So instead of adding this method |
||
try { | ||
// We only migrate the specific wallet folder because other wallets might be using the same .afj folder | ||
// which are used by different agents | ||
const oldWalletPath = this.dataPath.replace('.credo/data', `.afj/data/wallet/${walletId}`) | ||
const cacheAfjPath = this.cachePath.replace('.credo', '.afj') | ||
const tempAfjPath = this.tempPath.replace('.credo', '.afj') | ||
|
||
const pathsToMigrate = [ | ||
{ | ||
from: oldWalletPath, | ||
// We manually construct the path to the wallet folder because we only want to migrate the specific wallet | ||
to: this.dataPath + '/wallet/' + walletId, | ||
}, | ||
{ | ||
from: cacheAfjPath, | ||
to: this.cachePath, | ||
}, | ||
{ | ||
from: tempAfjPath, | ||
to: this.tempPath, | ||
}, | ||
] | ||
|
||
for await (const path of pathsToMigrate) { | ||
// Migrate if the old paths exist | ||
if (await this.exists(path.from)) { | ||
await this.copyDirectory(path.from, path.to) | ||
} | ||
} | ||
Comment on lines
+62
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we delete the old files afterwards? Or how do we clean up? |
||
} catch (error) { | ||
throw new CredoError(`Error during migration from .afj to .credo`, { | ||
cause: error, | ||
}) | ||
} | ||
} | ||
|
||
public async copyDirectory(sourcePath: string, destinationPath: string) { | ||
await cp(sourcePath, destinationPath, { recursive: true }) | ||
} | ||
|
||
public async exists(path: string) { | ||
|
@@ -49,7 +94,6 @@ export class NodeFileSystem implements FileSystem { | |
} | ||
|
||
public async write(path: string, data: string): Promise<void> { | ||
// Make sure parent directories exist | ||
await mkdir(dirname(path), { recursive: true }) | ||
|
||
return writeFile(path, data, { encoding: 'utf-8' }) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,26 +13,88 @@ export class ReactNativeFileSystem implements FileSystem { | |
* Create new ReactNativeFileSystem class instance. | ||
* | ||
* @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 | ||
* Files will be created under baseDataPath/.credo 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 | ||
* Files will be created under baseCachePath/.credo 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 | ||
* Files will be created under baseTempPath/.credo directory. If not specified, it will be set to | ||
* RNFS.TemporaryDirectoryPath | ||
* | ||
* @see https://github.com/itinance/react-native-fs#constants | ||
*/ | ||
public constructor(options?: { baseDataPath?: string; baseCachePath?: string; baseTempPath?: string }) { | ||
this.dataPath = `${options?.baseDataPath ?? RNFS.DocumentDirectoryPath}/.afj` | ||
this.dataPath = `${options?.baseDataPath ?? RNFS.DocumentDirectoryPath}/.credo` | ||
// In Android, TemporaryDirectoryPath falls back to CachesDirectoryPath | ||
this.cachePath = options?.baseCachePath | ||
? `${options?.baseCachePath}/.afj` | ||
: `${RNFS.CachesDirectoryPath}/.afj${Platform.OS === 'android' ? '/cache' : ''}` | ||
? `${options?.baseCachePath}/.credo` | ||
: `${RNFS.CachesDirectoryPath}/.credo${Platform.OS === 'android' ? '/cache' : ''}` | ||
this.tempPath = options?.baseTempPath | ||
? `${options?.baseTempPath}/.afj` | ||
: `${RNFS.TemporaryDirectoryPath}/.afj${Platform.OS === 'android' ? '/temp' : ''}` | ||
? `${options?.baseTempPath}/.credo` | ||
: `${RNFS.TemporaryDirectoryPath}/.credo${Platform.OS === 'android' ? '/temp' : ''}` | ||
} | ||
|
||
/** | ||
* Migrate data from .afj to .credo if .afj exists. | ||
* Copy the contents from old directory (.afj) to new directory (.credo). | ||
*/ | ||
public async migrateWalletToCredoFolder() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, this can be moved to migration logic so we don't have to duplicate |
||
try { | ||
const oldDataPath = this.dataPath.replace('.credo', `.afj`) | ||
const cacheAfjPath = this.cachePath.replace('.credo', '.afj') | ||
const tempAfjPath = this.tempPath.replace('.credo', '.afj') | ||
|
||
const pathsToMigrate = [ | ||
{ | ||
from: oldDataPath, | ||
to: this.dataPath, | ||
}, | ||
{ | ||
from: cacheAfjPath, | ||
to: this.cachePath, | ||
}, | ||
{ | ||
from: tempAfjPath, | ||
to: this.tempPath, | ||
}, | ||
] | ||
|
||
for await (const path of pathsToMigrate) { | ||
// Migrate if the old paths exist | ||
if (await this.exists(path.from)) { | ||
await this.copyDirectory(path.from, path.to) | ||
} | ||
} | ||
} catch (error) { | ||
throw new CredoError(`Error during migration from .afj to .credo`, { | ||
cause: error, | ||
}) | ||
} | ||
} | ||
|
||
public async copyDirectory(sourcePath: string, destinationPath: string) { | ||
try { | ||
// Ensure the target directory exists | ||
await RNFS.mkdir(destinationPath) | ||
|
||
// Get the contents of the source directory | ||
const contents = await RNFS.readDir(sourcePath) | ||
|
||
for (const item of contents) { | ||
const newPath = `${destinationPath}/${item.name}` | ||
|
||
if (item.isDirectory()) { | ||
// Recursively copy subdirectories | ||
await this.copyDirectory(item.path, newPath) | ||
} else { | ||
// Copy files to the new location | ||
await RNFS.copyFile(item.path, newPath) | ||
} | ||
} | ||
} catch (error) { | ||
throw new CredoError(`Error copying directory from ${sourcePath} to ${destinationPath}`, { cause: error }) | ||
} | ||
} | ||
|
||
public async exists(path: string): Promise<boolean> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
due to the sensivity in doing migrations it's always good to test these migrations extensively. For all other migrations and version changes we have tests (per migration method, and for a version migration in general), can you add these?