diff --git a/src/domain/buildingBlocks/Pluto.ts b/src/domain/buildingBlocks/Pluto.ts index 0f0315888..45d4f759b 100644 --- a/src/domain/buildingBlocks/Pluto.ts +++ b/src/domain/buildingBlocks/Pluto.ts @@ -8,6 +8,7 @@ import { PeerDID } from "../../peer-did/PeerDID"; import { uuid } from "@stablelib/uuid"; import { Arrayable } from "../../utils"; import * as Backup from "../backup"; +import { Startable } from "../protocols/Startable"; export namespace Pluto { /** @@ -32,19 +33,7 @@ export namespace Pluto { * which will be implemented using this SDK. Implement this interface using your * preferred underlying storage technology, most appropriate for your use case. */ -export interface Pluto { - // TODO extend Startable.Controller (changes the interface) - - /** - * Handle startup. - */ - start(): Promise; - - /** - * Handle teardown. - */ - stop?(): Promise; - +export interface Pluto extends Startable.IController { /** * create a Backup object from the stored data */ diff --git a/src/domain/buildingBlocks/Pollux.ts b/src/domain/buildingBlocks/Pollux.ts index 1091baea4..1644c6693 100644 --- a/src/domain/buildingBlocks/Pollux.ts +++ b/src/domain/buildingBlocks/Pollux.ts @@ -17,8 +17,8 @@ export type CredentialOfferJWTBasePayload = { options: { challenge: string; domain: string; - }; -}; + } +} export type CredentialOfferPayloads = { [CredentialType.AnonCreds]: Anoncreds.CredentialOfferType; @@ -50,7 +50,7 @@ export type ProcessedCredentialOfferPayloads = { export interface Pollux { revealCredentialFields: (credential: Credential, fields: string[], linkSecret?: string) => Promise<{ - [name: string]: any; + [name: string]: any }>; isCredentialRevoked: (credential: Credential) => Promise; @@ -71,12 +71,12 @@ export interface Pollux { presentationDefinition: PresentationDefinitionRequest, credential: Credential, privateKey: PrivateKey - ): Promise>; + ): Promise> createPresentationSubmission( presentationDefinition: PresentationDefinitionRequest, credential: Credential, privateKey: LinkSecret - ): Promise>; + ): Promise> /** * Process a PresentationSubmission, resolve the issuer did and verify the credential and the holder signature @@ -88,15 +88,15 @@ export interface Pollux { verifyPresentationSubmission( presentationSubmission: PresentationSubmission, options?: Pollux.verifyPresentationSubmission.options.JWT - ): Promise; + ): Promise verifyPresentationSubmission( presentationSubmission: PresentationSubmission, options?: Pollux.verifyPresentationSubmission.options.Anoncreds - ): Promise; + ): Promise verifyPresentationSubmission( presentationSubmission: PresentationSubmission, options?: Pollux.verifyPresentationSubmission.options.JWT | Pollux.verifyPresentationSubmission.options.Anoncreds - ): Promise; + ): Promise /** * Creates a PresentationDefinitionRequest object for oob Verifications @@ -108,7 +108,7 @@ export interface Pollux { type: T, claims: PresentationClaims, options: PresentationOptions - ): Promise>; + ): Promise> @@ -138,12 +138,12 @@ export namespace Pollux { export interface JWT { presentationDefinitionRequest: PresentationDefinitionRequest, challenge?: string, - domain?: string; + domain?: string } export interface SDJWT { issuer: DID, presentationDefinitionRequest: PresentationDefinitionRequest, - requiredClaims?: string[]; + requiredClaims?: string[] } } } @@ -159,7 +159,7 @@ export namespace Pollux { } export interface SDJWT { privateKey: PrivateKey; - presentationFrame: Record; + presentationFrame: Record } } } diff --git a/src/domain/index.ts b/src/domain/index.ts index 8711f5afa..ba706bbf0 100644 --- a/src/domain/index.ts +++ b/src/domain/index.ts @@ -1,4 +1,5 @@ export * as Backup from "./backup"; +export * as Protocols from "./protocols"; export * from "./models"; export * from "./protocols"; export * from "./buildingBlocks/Apollo"; diff --git a/src/domain/protocols/Startable.ts b/src/domain/protocols/Startable.ts new file mode 100644 index 000000000..1059550cb --- /dev/null +++ b/src/domain/protocols/Startable.ts @@ -0,0 +1,82 @@ +/** + * Define controls for managing entity lifecycle. + */ +export namespace Startable { + /** + * states for a Startable entity + */ + export enum State { + STOPPED = "stopped", + STARTING = "starting", + RUNNING = "running", + STOPPING = "stopping", + } + + /** + * define the structure of a Startable entity + */ + export interface IController { + /** + * current status of the entity + */ + state: State; + /** + * handle the startup of an entity + * + * updates `state` according to lifecycle + * + * @returns {Promise} + */ + start(): Promise; + /** + * handle the teardown of an entity + * + * updates `state` according to lifecycle + * + * @returns {Promise} + */ + stop(): Promise; + } + + export abstract class Controller implements IController { + public state = State.STOPPED; + + /** + * internal method to define specific startup routine + * + * used by `start()` internally + * + * implement with `protected` to keep hidden from class interface + */ + protected abstract _start(): Promise; + + /** + * internal method to define teardown routine + * + * used by `stop()` internally + * + * implement with `protected` to keep hidden from class interface + */ + protected abstract _stop(): Promise; + + async start(): Promise { + if (this.state === Startable.State.STOPPED) { + this.state = Startable.State.STARTING; + await this._start(); + this.state = Startable.State.RUNNING; + } + + return this.state; + } + + async stop(): Promise { + if (this.state === Startable.State.RUNNING) { + this.state = Startable.State.STOPPING; + await this._stop(); + this.state = Startable.State.STOPPED; + } + + return this.state; + } + } +} diff --git a/src/domain/protocols/index.ts b/src/domain/protocols/index.ts index 4585256e1..a21b2f6cf 100644 --- a/src/domain/protocols/index.ts +++ b/src/domain/protocols/index.ts @@ -1 +1,2 @@ export * from "./KeyRestoration"; +export * from "./Startable"; diff --git a/src/edge-agent/Agent.ts b/src/edge-agent/Agent.ts index 4dea74c8e..363b9fbfb 100644 --- a/src/edge-agent/Agent.ts +++ b/src/edge-agent/Agent.ts @@ -2,12 +2,12 @@ import * as Domain from "../domain"; import Apollo from "../apollo"; import Castor from "../castor"; import Pollux from "../pollux"; +import { Startable } from "../domain/protocols/Startable"; import { AgentBackup } from "./Agent.Backup"; import { SignWithDID } from "./didFunctions/Sign"; import { CreatePrismDID } from "./didFunctions/CreatePrismDID"; import { FetchApi } from "./helpers/FetchApi"; import { Task } from "../utils/tasks"; -import { Startable } from "../utils/startable"; import { notNil } from "../utils"; /** @@ -17,14 +17,7 @@ import { notNil } from "../utils"; * @class Agent * @typedef {Agent} */ -export default class Agent implements Startable.Controller { - /** - * Agent state - * - * @public - * @type {Startable.State} - */ - public state = Startable.State.STOPPED; +export default class Agent extends Startable.Controller { public backup: AgentBackup; public readonly pollux: Pollux; @@ -45,6 +38,7 @@ export default class Agent implements Startable.Controller { public readonly seed: Domain.Seed = apollo.createRandomSeed().seed, public readonly api: Domain.Api = new FetchApi(), ) { + super(); this.pollux = new Pollux(apollo, castor); this.backup = new AgentBackup(this); } @@ -78,31 +72,17 @@ export default class Agent implements Startable.Controller { return agent; } - /** - * Asyncronously start the agent - * - * @returns {Promise} - */ - start(): Promise { - return Startable.start(this, async () => { - await this.pluto.start(); - await this.pollux.start(); - }); + protected async _start() { + await this.pluto.start(); + await this.pollux.start(); } - /** - * Asyncronously stop the agent and any side task that is running - * - * @returns {Promise} - */ - stop(): Promise { - return Startable.stop(this, async () => { - await this.pollux.stop(); + protected async _stop() { + await this.pollux.stop(); - if (notNil(this.pluto.stop)) { - await this.pluto.stop(); - } - }); + if (notNil(this.pluto.stop)) { + await this.pluto.stop(); + } } /** diff --git a/src/edge-agent/didcomm/Agent.ts b/src/edge-agent/didcomm/Agent.ts index dfea7e6b2..fafe465fa 100644 --- a/src/edge-agent/didcomm/Agent.ts +++ b/src/edge-agent/didcomm/Agent.ts @@ -37,7 +37,7 @@ import { FetchApi } from "../helpers/FetchApi"; import { ParsePrismInvitation } from "./ParsePrismInvitation"; import { ParseInvitation } from "./ParseInvitation"; import { HandleOOBInvitation } from "./HandleOOBInvitation"; -import { Startable } from "../../utils/startable"; +import { Startable } from "../../domain/protocols/Startable"; import { notNil } from "../../utils"; /** @@ -47,8 +47,7 @@ import { notNil } from "../../utils"; * @class Agent * @typedef {Agent} */ -export default class DIDCommAgent implements Startable.Controller { - public state = Startable.State.STOPPED; +export default class DIDCommAgent extends Startable.Controller { public backup: AgentBackup; public readonly pollux: Pollux; @@ -71,6 +70,7 @@ export default class DIDCommAgent implements Startable.Controller { public readonly api: Domain.Api = new FetchApi(), options?: AgentOptions ) { + super(); this.pollux = new Pollux(apollo, castor); this.backup = new AgentBackup(this); } @@ -137,55 +137,45 @@ export default class DIDCommAgent implements Startable.Controller { return agent; } - start(): Promise { - return Startable.start(this, async () => { - try { - this.state = Startable.State.STARTING; - await this.pluto.start(); - await this.pollux.start(); - await this.connectionManager.startMediator(); - } - catch (e) { - if (e instanceof Domain.AgentError.NoMediatorAvailableError) { - const hostDID = await this.createNewPeerDID([], false); - await this.connectionManager.registerMediator(hostDID); - } - else { - throw e; - } - } - - if (this.connectionManager.mediationHandler.mediator !== undefined) { - await this.connectionManager.startFetchingMessages(5); + protected async _start() { + try { + await this.pluto.start(); + await this.pollux.start(); + await this.connectionManager.startMediator(); + } + catch (e) { + if (e instanceof Domain.AgentError.NoMediatorAvailableError) { + const hostDID = await this.createNewPeerDID([], false); + await this.connectionManager.registerMediator(hostDID); } else { - throw new Domain.AgentError.MediationRequestFailedError("Mediation failed"); + throw e; } + } - const storedLinkSecret = await this.pluto.getLinkSecret(); - if (storedLinkSecret == null) { - const secret = this.pollux.anoncreds.createLinksecret(); - const linkSecret = new Domain.LinkSecret(secret); - await this.pluto.storeLinkSecret(linkSecret); - } - }); + if (this.connectionManager.mediationHandler.mediator !== undefined) { + await this.connectionManager.startFetchingMessages(5); + } + else { + throw new Domain.AgentError.MediationRequestFailedError("Mediation failed"); + } + + const storedLinkSecret = await this.pluto.getLinkSecret(); + if (storedLinkSecret == null) { + const secret = this.pollux.anoncreds.createLinksecret(); + const linkSecret = new Domain.LinkSecret(secret); + await this.pluto.storeLinkSecret(linkSecret); + } } - /** - * Asyncronously stop the agent and dependencies - * - * @returns {Promise} - */ - stop(): Promise { - return Startable.stop(this, async () => { - await this.connectionManager.stopAllEvents(); - await this.connectionManager.stopFetchingMessages(); - await this.pollux.stop(); - - if (notNil(this.pluto.stop)) { - await this.pluto.stop(); - } - }); + protected async _stop() { + await this.connectionManager.stopAllEvents(); + await this.connectionManager.stopFetchingMessages(); + await this.pollux.stop(); + + if (notNil(this.pluto.stop)) { + await this.pluto.stop(); + } } /** diff --git a/src/edge-agent/oidc/Agent.ts b/src/edge-agent/oidc/Agent.ts index 92e3b8119..4c6924fb0 100644 --- a/src/edge-agent/oidc/Agent.ts +++ b/src/edge-agent/oidc/Agent.ts @@ -7,13 +7,13 @@ import { AuthorizationRequest } from "./protocols/AuthorizationRequest"; import { TokenResponse } from "./protocols/TokenResponse"; import { TokenRequest } from "./protocols/TokenRequest"; import { CredentialRequest } from "./protocols/CredentialRequest"; +import { Startable } from "../../domain/protocols/Startable"; import { FetchApi } from "../helpers/FetchApi"; import { Task } from "../../utils/tasks"; import * as DIDfns from "../didFunctions"; import * as Tasks from "./tasks"; import * as Errors from "./errors"; import { JsonObj, expect, notNil } from "../../utils"; -import { Startable } from "../../utils/startable"; /** * https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html @@ -28,9 +28,8 @@ class Connection { ) {} } -export class OIDCAgent implements Startable.Controller { +export class OIDCAgent extends Startable.Controller { private connections: Connection[] = []; - public state = Startable.State.STOPPED; public readonly pollux: Pollux; constructor( @@ -40,6 +39,7 @@ export class OIDCAgent implements Startable.Controller { public readonly seed?: Domain.Seed, public readonly api?: Domain.Api, ) { + super(); this.pollux = new Pollux(apollo, castor); this.seed = seed ?? apollo.createRandomSeed().seed; this.api = api ?? new FetchApi(); @@ -74,21 +74,17 @@ export class OIDCAgent implements Startable.Controller { return agent; } - start(): Promise { - return Startable.start(this, async () => { - await this.pluto.start(); - await this.pollux.start(); - }); + protected async _start() { + await this.pluto.start(); + await this.pollux.start(); } - async stop(): Promise { - return Startable.stop(this, async () => { - await this.pollux.stop(); + protected async _stop() { + await this.pollux.stop(); - if (notNil(this.pluto.stop)) { - await this.pluto.stop(); - } - }); + if (notNil(this.pluto.stop)) { + await this.pluto.stop(); + } } private runTask(task: Task): Promise { diff --git a/src/pluto/Pluto.ts b/src/pluto/Pluto.ts index a990443df..9bea222a6 100644 --- a/src/pluto/Pluto.ts +++ b/src/pluto/Pluto.ts @@ -5,7 +5,7 @@ import { PeerDID } from "../peer-did/PeerDID"; import { BackupManager } from "./backup/BackupManager"; import { PlutoRepositories, repositoryFactory } from "./repositories"; import { Arrayable, asArray, notNil } from "../utils"; -import { Startable } from "../utils/startable"; +import { Startable } from "../domain/protocols/Startable"; import { Version } from "../domain/backup"; @@ -109,8 +109,7 @@ export namespace Pluto { } } -export class Pluto implements Domain.Pluto { - public state = Startable.State.STOPPED; +export class Pluto extends Startable.Controller implements Domain.Pluto { public BackupMgr: BackupManager; private Repositories: PlutoRepositories; @@ -118,24 +117,21 @@ export class Pluto implements Domain.Pluto { private readonly store: Pluto.Store, private readonly keyRestoration: Domain.KeyRestoration ) { + super(); this.Repositories = repositoryFactory(store, keyRestoration); this.BackupMgr = new BackupManager(this, this.Repositories); } - async start() { - await Startable.start(this, async () => { - if (notNil(this.store.start)) { - await this.store.start(); - } - }); + protected async _start() { + if (notNil(this.store.start)) { + await this.store.start(); + } } - async stop() { - await Startable.stop(this, async () => { - if (notNil(this.store.stop)) { - await this.store.stop(); - } - }); + protected async _stop() { + if (notNil(this.store.stop)) { + await this.store.stop(); + } } /** Backups **/ diff --git a/src/pollux/Pollux.ts b/src/pollux/Pollux.ts index 8eb6c19f4..480735e77 100644 --- a/src/pollux/Pollux.ts +++ b/src/pollux/Pollux.ts @@ -66,7 +66,7 @@ import { VerificationKeyType } from "../castor/types"; import { revocationJsonldDocuments } from "../domain/models/revocation"; import { Bitstring } from "./utils/Bitstring"; import { defaultHashConfig } from "./utils/jwt/config"; -import { Startable } from "../utils/startable"; +import { Startable } from "../domain/protocols/Startable"; /** * Implementation of Pollux @@ -75,11 +75,10 @@ import { Startable } from "../utils/startable"; * @class Pollux * @typedef {Pollux} */ -export default class Pollux implements IPollux, Startable.Controller { +export default class Pollux extends Startable.Controller implements IPollux { private _anoncreds: AnoncredsLoader | undefined; private _jwe: typeof import("jwe-wasm") | undefined; private _pako = pako; - public state = Startable.State.STOPPED; constructor( private apollo: Apollo, @@ -88,6 +87,7 @@ export default class Pollux implements IPollux, Startable.Controller { private JWT = new JWTClass(apollo, castor), private SDJWT = new SDJWTClass(apollo, castor) ) { + super(); } get anoncreds() { @@ -104,20 +104,16 @@ export default class Pollux implements IPollux, Startable.Controller { return this._jwe; } - start(): Promise { - return Startable.start(this, async () => { - this._anoncreds = await AnoncredsLoader.getInstance(); - this._jwe ??= await import("jwe-wasm").then(async module => { - const wasmInstance = module.initSync({ module: wasmBuffer }); - await module.default(wasmInstance); - return module; - }); + protected async _start() { + this._anoncreds = await AnoncredsLoader.getInstance(); + this._jwe ??= await import("jwe-wasm").then(async module => { + const wasmInstance = module.initSync({ module: wasmBuffer }); + await module.default(wasmInstance); + return module; }); } - async stop(): Promise { - return Startable.stop(this, async () => {}); - } + protected async _stop() {} async revealCredentialFields( credential: Credential, diff --git a/src/utils/startable.ts b/src/utils/startable.ts deleted file mode 100644 index 13d6000c7..000000000 --- a/src/utils/startable.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Define controls for managing entity lifecycle. - */ -export namespace Startable { - /** - * states for a Startable entity - */ - export enum State { - STOPPED = "stopped", - STARTING = "starting", - RUNNING = "running", - STOPPING = "stopping", - } - - /** - * - */ - export interface Controller { - /** - * current status of the entity - */ - state: State; - - /** - * handle startup if entity is in STOPPED state - * - * - * @returns {Promise} - */ - start(): Promise; - - /** - * handle teardown if entity is in RUNNING state - * - * @returns {Promise} - */ - stop(): Promise; - } - - /** - * handle the startup of a Controller - * updating state lifecycle around running the delegate - * - * @param obj - * @param delegate - * @returns - */ - export const start = async (obj: Pick, delegate: () => Promise): Promise => { - if (obj.state === Startable.State.STOPPED) { - obj.state = Startable.State.STARTING; - await delegate(); - obj.state = Startable.State.RUNNING; - } - - return obj.state; - }; - - /** - * handle the teardown of a Controller - * updating state lifecycle around running the delegate - * - * @param obj - * @param delegate - * @returns - */ - export const stop = async (obj: Pick, delegate: () => Promise): Promise => { - if (obj.state === Startable.State.RUNNING) { - obj.state = Startable.State.STOPPING; - await delegate(); - obj.state = Startable.State.STOPPED; - } - - return obj.state; - }; - -}