diff --git a/packages/server/src/commands/ImportContentCommand.ts b/packages/server/src/commands/ImportContentCommand.ts deleted file mode 100644 index 05b7d036dd0..00000000000 --- a/packages/server/src/commands/ImportContentCommand.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { PositionalOptions, Options } from 'yargs'; -import Logger from '@joplin/lib/Logger'; -import BaseCommand, { RunContext } from './BaseCommand'; -import parseStorageConnectionString from '../models/items/storage/parseStorageConnectionString'; - -const logger = Logger.create('ImportContentCommand'); - -interface Argv { - toStorage: string; - batchSize?: number; -} - -export default class ImportContentCommand extends BaseCommand { - - public command() { - return 'import-content '; - } - - public description() { - return 'import content to storage'; - } - - public positionals(): Record { - return { - 'to-storage': { - description: 'storage connection string', - type: 'string', - }, - }; - } - - public options(): Record { - return { - 'batch-size': { - type: 'number', - description: 'Item batch size', - }, - }; - } - - public async run(argv: Argv, runContext: RunContext): Promise { - const toStorageConfig = parseStorageConnectionString(argv.toStorage); - const batchSize = argv.batchSize || 1000; - - logger.info('Importing to storage:', toStorageConfig); - logger.info(`Batch size: ${batchSize}`); - - await runContext.models.item().importContentToStorage(toStorageConfig, { - batchSize: batchSize || 1000, - logger: logger as Logger, - }); - } - -} diff --git a/packages/server/src/commands/StorageCommand.ts b/packages/server/src/commands/StorageCommand.ts new file mode 100644 index 00000000000..b4192e2e958 --- /dev/null +++ b/packages/server/src/commands/StorageCommand.ts @@ -0,0 +1,108 @@ +import { PositionalOptions, Options } from 'yargs'; +import Logger from '@joplin/lib/Logger'; +import BaseCommand, { RunContext } from './BaseCommand'; +import parseStorageConnectionString from '../models/items/storage/parseStorageConnectionString'; +import loadStorageDriver from '../models/items/storage/loadStorageDriver'; +import uuidgen from '../utils/uuidgen'; +import { Context } from '../models/items/storage/StorageDriverBase'; + +const logger = Logger.create('ImportContentCommand'); + +enum ArgvCommand { + Import = 'import', + CheckConnection = 'check-connection', +} + +interface Argv { + command: ArgvCommand; + connection: string; + batchSize?: number; +} + +export default class StorageCommand extends BaseCommand { + + public command() { + return 'storage '; + } + + public description() { + return 'import content to storage'; + } + + public positionals(): Record { + return { + 'command': { + description: 'command to execute', + choices: [ + ArgvCommand.Import, + ArgvCommand.CheckConnection, + ], + }, + }; + } + + public options(): Record { + return { + 'batch-size': { + type: 'number', + description: 'Item batch size', + }, + 'connection': { + description: 'storage connection string', + type: 'string', + }, + }; + } + + public async run(argv: Argv, runContext: RunContext): Promise { + const commands: Record = { + [ArgvCommand.Import]: async () => { + if (!argv.connection) throw new Error('--connection option is required'); + + const toStorageConfig = parseStorageConnectionString(argv.connection); + const batchSize = argv.batchSize || 1000; + + logger.info('Importing to storage:', toStorageConfig); + logger.info(`Batch size: ${batchSize}`); + + await runContext.models.item().importContentToStorage(toStorageConfig, { + batchSize: batchSize || 1000, + logger: logger as Logger, + }); + }, + + [ArgvCommand.CheckConnection]: async () => { + const storageConfig = parseStorageConnectionString(argv.connection); + const driver = await loadStorageDriver(storageConfig, runContext.db, { assignDriverId: false }); + const itemId = `testingconnection${uuidgen(8)}`; + const itemContent = Buffer.from(uuidgen(8)); + const context: Context = { models: runContext.models }; + + try { + await driver.write(itemId, itemContent, context); + } catch (error) { + error.message = `Could not write content to storage: ${error.message}`; + throw error; + } + + if (!(await driver.exists(itemId, context))) { + throw new Error(`Written item does not exist: ${itemId}`); + } + + const readContent = await driver.read(itemId, context); + if (readContent.toString() !== itemContent.toString()) throw new Error(`Could not read back written item. Expected: ${itemContent.toString()}. Got: ${readContent.toString()}`); + + await driver.delete(itemId, context); + + if (await driver.exists(itemId, context)) { + throw new Error(`Deleted item still exist: ${itemId}`); + } + + logger.info('Item was written, read back and deleted without any error.'); + }, + }; + + await commands[argv.command](); + } + +} diff --git a/packages/server/src/utils/setupCommands.ts b/packages/server/src/utils/setupCommands.ts index 27ab72ab1f0..2ff7ecc1a33 100644 --- a/packages/server/src/utils/setupCommands.ts +++ b/packages/server/src/utils/setupCommands.ts @@ -2,7 +2,7 @@ import yargs = require('yargs'); import BaseCommand from '../commands/BaseCommand'; import DbCommand from '../commands/DbCommand'; import DeleteOldChangesCommand from '../commands/DeleteOldChangesCommand'; -import ImportContentCommand from '../commands/ImportContentCommand'; +import StorageCommand from '../commands/StorageCommand'; import MigrateCommand from '../commands/MigrateCommand'; export interface Commands { @@ -17,7 +17,7 @@ export default async function setupCommands(): Promise { new MigrateCommand(), new DbCommand(), new DeleteOldChangesCommand(), - new ImportContentCommand(), + new StorageCommand(), ]; for (const cmd of commands) {