diff --git a/src/cli/BaseCommand.ts b/src/cli/BaseCommand.ts index 23a437e87..2cc0335e1 100644 --- a/src/cli/BaseCommand.ts +++ b/src/cli/BaseCommand.ts @@ -1,4 +1,5 @@ import { ApplicationError } from "@js-soft/ts-utils"; +import _ from "lodash"; import yargs from "yargs"; import { ConnectorRuntime } from "../ConnectorRuntime"; import { ConnectorRuntimeConfig } from "../ConnectorRuntimeConfig"; @@ -60,11 +61,11 @@ export abstract class BaseCommand { } } - protected async createRuntime(): Promise { + protected async createRuntime(allowIdentityCreation = false): Promise { if (this.#cliRuntime) return; if (!this.#connectorConfig) throw new Error("Connector config not initialized"); - this.#cliRuntime = await ConnectorRuntime.create(this.#connectorConfig); + this.#cliRuntime = await ConnectorRuntime.create(_.merge(this.#connectorConfig, { transportLibrary: { allowIdentityCreation } })); await this.#cliRuntime.start(); } diff --git a/src/cli/commands/identity/init.ts b/src/cli/commands/identity/init.ts new file mode 100644 index 000000000..d7fa01277 --- /dev/null +++ b/src/cli/commands/identity/init.ts @@ -0,0 +1,30 @@ +import { CoreError } from "@nmshd/core-types"; +import { CommandModule } from "yargs"; +import { BaseCommand, ConfigFileOptions, configOptionBuilder } from "../../BaseCommand"; + +export const identityInitHandler = async ({ config }: ConfigFileOptions): Promise => { + await new IdentityInit().run(config); +}; +export const yargsIdentityInitCommand: CommandModule<{}, ConfigFileOptions> = { + command: "init", + describe: "initialize the identity", + handler: identityInitHandler, + builder: configOptionBuilder +}; + +export default class IdentityInit extends BaseCommand { + protected async runInternal(): Promise { + try { + await this.createRuntime(false); + this.log.log("Identity already exists not creating a new one."); + } catch (error: unknown) { + if (error instanceof CoreError && error.code === "error.transport.general.noIdentityFound") { + await this.createRuntime(true); + const address = (await this.cliRuntime.getServices().transportServices.account.getIdentityInfo()).value.address; + this.log.log(`Identity with address (${address}) created successfully.`); + } else { + throw error; + } + } + } +} diff --git a/src/cli/commands/index.ts b/src/cli/commands/index.ts index 9ac0289cd..fd4ec786a 100644 --- a/src/cli/commands/index.ts +++ b/src/cli/commands/index.ts @@ -1,3 +1,4 @@ +export * from "./identity/init"; export * from "./identity/status"; export * from "./identityDeletion/cancel"; export * from "./identityDeletion/init"; diff --git a/src/index.ts b/src/index.ts index 214c7de6e..c7a29d5fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,14 +2,14 @@ import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { startConnectorCommand, yargsIdentityDeletionCancelCommand, yargsIdentityDeletionInitCommand, yargsIdentityStatusCommand } from "./cli/commands"; +import { startConnectorCommand, yargsIdentityDeletionCancelCommand, yargsIdentityDeletionInitCommand, yargsIdentityInitCommand, yargsIdentityStatusCommand } from "./cli/commands"; yargs(hideBin(process.argv)) .command({ command: "identity [command]", describe: "Identity related commands", builder: (yargs) => { - return yargs.command(yargsIdentityStatusCommand); + return yargs.command(yargsIdentityStatusCommand).command(yargsIdentityInitCommand); }, handler: () => { yargs.showHelp("log"); diff --git a/test/modules/cli/identity/IdentityInit.test.ts b/test/modules/cli/identity/IdentityInit.test.ts new file mode 100644 index 000000000..c9ad931cb --- /dev/null +++ b/test/modules/cli/identity/IdentityInit.test.ts @@ -0,0 +1,33 @@ +import { identityInitHandler } from "../../../../dist/cli/commands"; +import { resetDB, setupEnvironment } from "../setup"; + +describe("identity init", () => { + const identityCreatedPattern = /Identity with address did:e:((([A-Za-z0-9]+(-[A-Za-z0-9]+)*)\.)+[a-z]{2,}|localhost):dids:[0-9a-f]{22} created successfully\./; + + beforeAll(() => { + setupEnvironment(); + }); + + afterAll(async () => { + await resetDB(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + test("identity should be created", async () => { + const consoleSpy = jest.spyOn(console, "log"); + await identityInitHandler({ config: undefined }); + expect(consoleSpy.mock.lastCall![0]).toMatch(identityCreatedPattern); + expect(consoleSpy).toHaveBeenCalledTimes(1); + }); + + test("identity should not be created when one already exists", async () => { + const consoleSpy = jest.spyOn(console, "log"); + await identityInitHandler({ config: undefined }); + await identityInitHandler({ config: undefined }); + expect(consoleSpy).toHaveBeenCalledWith("Identity already created!"); + expect(consoleSpy).toHaveBeenCalledTimes(2); + }); +}); diff --git a/test/modules/cli/identity/identityStatus.test.ts b/test/modules/cli/identity/identityStatus.test.ts index 56a06f48c..29428fb84 100644 --- a/test/modules/cli/identity/identityStatus.test.ts +++ b/test/modules/cli/identity/identityStatus.test.ts @@ -1,12 +1,13 @@ import { sleep } from "@js-soft/ts-utils"; -import { identityDeletionInitHandler, identityStatusHandler } from "../../../../dist/cli/commands"; +import { identityDeletionInitHandler, identityInitHandler, identityStatusHandler } from "../../../../dist/cli/commands"; import { resetDB, setupEnvironment } from "../setup"; describe("Identity status", () => { const identityStatusPattern = /Identity Address: did:e:((([A-Za-z0-9]+(-[A-Za-z0-9]+)*)\.)+[a-z]{2,}|localhost):dids:[0-9a-f]{22}/; - beforeAll(() => { + beforeAll(async () => { setupEnvironment(); + await identityInitHandler({}); }); afterAll(async () => { diff --git a/test/modules/cli/identityDeletion/identityDeletion.test.ts b/test/modules/cli/identityDeletion/identityDeletion.test.ts index 5cc2fc995..59ebfa08e 100644 --- a/test/modules/cli/identityDeletion/identityDeletion.test.ts +++ b/test/modules/cli/identityDeletion/identityDeletion.test.ts @@ -1,9 +1,10 @@ -import { identityDeletionCancelHandler, identityDeletionInitHandler } from "../../../../dist/cli/commands"; +import { identityDeletionCancelHandler, identityDeletionInitHandler, identityInitHandler } from "../../../../dist/cli/commands"; import { resetDB, setupEnvironment } from "../setup"; describe("Identity deletion", () => { - beforeAll(() => { + beforeAll(async () => { setupEnvironment(); + await identityInitHandler({}); }); afterAll(async () => {