Skip to content

Commit

Permalink
feat: implementing Startable and propagating stop() (#309)
Browse files Browse the repository at this point in the history
Signed-off-by: Curtish <ch@curtish.me>
  • Loading branch information
curtis-h authored Jan 7, 2025
1 parent 6745ea3 commit 9e459c5
Show file tree
Hide file tree
Showing 15 changed files with 506 additions and 256 deletions.
3 changes: 3 additions & 0 deletions src/domain/buildingBlocks/Pluto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ export namespace Pluto {
* preferred underlying storage technology, most appropriate for your use case.
*/
export interface Pluto {
// TODO apply breaking change below
// export interface Pluto extends Startable.IController {
/**
* Pluto initialise function
*/
start(): Promise<void>;
stop?(): Promise<void>;

/**
* create a Backup object from the stored data
Expand Down
1 change: 1 addition & 0 deletions src/domain/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * as Backup from "./backup";
export * as Protocols from "./protocols";
export * from "./models";
export * from "./protocols";
export * from "./buildingBlocks/Apollo";
Expand Down
82 changes: 82 additions & 0 deletions src/domain/protocols/Startable.ts
Original file line number Diff line number Diff line change
@@ -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<State>}
*/
start(): Promise<State>;
/**
* handle the teardown of an entity
*
* updates `state` according to lifecycle
*
* @returns {Promise<State>}
*/
stop(): Promise<State>;
}

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<void>;

/**
* internal method to define teardown routine
*
* used by `stop()` internally
*
* implement with `protected` to keep hidden from class interface
*/
protected abstract _stop(): Promise<void>;

async start(): Promise<State> {
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<State> {
if (this.state === Startable.State.RUNNING) {
this.state = Startable.State.STOPPING;
await this._stop();
this.state = Startable.State.STOPPED;
}

return this.state;
}
}
}
1 change: 1 addition & 0 deletions src/domain/protocols/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./KeyRestoration";
export * from "./Startable";
51 changes: 12 additions & 39 deletions src/edge-agent/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ 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";

enum AgentState {
STOPPED = "stopped",
STARTING = "starting",
RUNNING = "running",
STOPPING = "stopping",
}
import { notNil } from "../utils";

/**
* Edge agent implementation
Expand All @@ -22,14 +17,7 @@ enum AgentState {
* @class Agent
* @typedef {Agent}
*/
export default class Agent {
/**
* Agent state
*
* @public
* @type {AgentState}
*/
public state: AgentState = AgentState.STOPPED;
export default class Agent extends Startable.Controller {
public backup: AgentBackup;
public readonly pollux: Pollux;

Expand All @@ -50,6 +38,7 @@ export default class Agent {
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);
}
Expand Down Expand Up @@ -83,33 +72,17 @@ export default class Agent {
return agent;
}

/**
* Asyncronously start the agent
*
* @async
* @returns {Promise<AgentState>}
*/
async start(): Promise<AgentState> {
if (this.state === AgentState.STOPPED) {
this.state = AgentState.STARTING;
await this.pluto.start();
await this.pollux.start();
}

return this.state;
protected async _start() {
await this.pluto.start();
await this.pollux.start();
}

/**
* Asyncronously stop the agent and any side task that is running
*
* @async
* @returns {Promise<void>}
*/
async stop(): Promise<void> {
if (this.state !== AgentState.RUNNING) {
return;
protected async _stop() {
await this.pollux.stop();

if (notNil(this.pluto.stop)) {
await this.pluto.stop();
}
this.state = AgentState.STOPPED;
}

/**
Expand Down
68 changes: 19 additions & 49 deletions src/edge-agent/didcomm/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,8 @@ import { FetchApi } from "../helpers/FetchApi";
import { ParsePrismInvitation } from "./ParsePrismInvitation";
import { ParseInvitation } from "./ParseInvitation";
import { HandleOOBInvitation } from "./HandleOOBInvitation";

enum AgentState {
STOPPED = "stopped",
STARTING = "starting",
RUNNING = "running",
STOPPING = "stopping",
}
import { Startable } from "../../domain/protocols/Startable";
import { notNil } from "../../utils";

/**
* Edge agent implementation
Expand All @@ -52,14 +47,7 @@ enum AgentState {
* @class Agent
* @typedef {Agent}
*/
export default class DIDCommAgent {
/**
* Agent state
*
* @public
* @type {AgentState}
*/
public state: AgentState = AgentState.STOPPED;
export default class DIDCommAgent extends Startable.Controller {
public backup: AgentBackup;
public readonly pollux: Pollux;

Expand All @@ -82,6 +70,7 @@ export default class DIDCommAgent {
public readonly api: Domain.Api = new FetchApi(),
options?: AgentOptions

Check warning on line 71 in src/edge-agent/didcomm/Agent.ts

View workflow job for this annotation

GitHub Actions / Build and test

'options' is defined but never used. Allowed unused args must match /^_/u
) {
super();
this.pollux = new Pollux(apollo, castor);
this.backup = new AgentBackup(this);
}
Expand Down Expand Up @@ -148,36 +137,26 @@ export default class DIDCommAgent {
return agent;
}


/**
* Asyncronously start the agent
*
* @async
* @returns {Promise<AgentState>}
*/
async start(): Promise<AgentState> {
if (this.state !== AgentState.STOPPED) {
return this.state;
}

protected async _start() {
try {
this.state = AgentState.STARTING;
await this.pluto.start();
await this.pollux.start();
await this.connectionManager.startMediator();
} catch (e) {
}
catch (e) {
if (e instanceof Domain.AgentError.NoMediatorAvailableError) {
const hostDID = await this.createNewPeerDID([], false);

await this.connectionManager.registerMediator(hostDID);

} else throw e;
}
else {
throw e;
}
}

if (this.connectionManager.mediationHandler.mediator !== undefined) {
await this.connectionManager.startFetchingMessages(5);
this.state = AgentState.RUNNING;
} else {
}
else {
throw new Domain.AgentError.MediationRequestFailedError("Mediation failed");
}

Expand All @@ -187,25 +166,16 @@ export default class DIDCommAgent {
const linkSecret = new Domain.LinkSecret(secret);
await this.pluto.storeLinkSecret(linkSecret);
}

return this.state;
}

/**
* Asyncronously stop the agent and any side task that is running
*
* @async
* @returns {Promise<void>}
*/
async stop(): Promise<void> {
if (this.state !== AgentState.RUNNING) {
return;
}
this.state = AgentState.STOPPING;
protected async _stop() {
await this.connectionManager.stopAllEvents();
await this.connectionManager.stopFetchingMessages();
// await this.agent.stop();
this.state = AgentState.STOPPED;
await this.pollux.stop();

if (notNil(this.pluto.stop)) {
await this.pluto.stop();
}
}

/**
Expand Down
36 changes: 14 additions & 22 deletions src/edge-agent/oidc/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +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 } from "../../utils";
import { JsonObj, expect, notNil } from "../../utils";

/**
* https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
Expand All @@ -24,20 +25,11 @@ class Connection {
public readonly issuerMeta: OIDC.IssuerMetadata,
public readonly scopes: string[],
public readonly tokenResponse: TokenResponse,
) { }
) {}
}

// TODO make startable interface
enum AgentState {
STOPPED = "stopped",
STARTING = "starting",
RUNNING = "running",
STOPPING = "stopping",
}

export class OIDCAgent {
export class OIDCAgent extends Startable.Controller {
private connections: Connection[] = [];
public state = AgentState.STOPPED;
public readonly pollux: Pollux;

constructor(
Expand All @@ -47,6 +39,7 @@ export class OIDCAgent {
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();
Expand Down Expand Up @@ -81,18 +74,17 @@ export class OIDCAgent {
return agent;
}

async start() {
if (this.state === AgentState.STOPPED) {
this.state = AgentState.STARTING;
await this.pluto.start();
await this.pollux.start();
this.state = AgentState.RUNNING;
}
return this.state;
protected async _start() {
await this.pluto.start();
await this.pollux.start();
}

async stop(): Promise<void> {
this.state = AgentState.STOPPED
protected async _stop() {
await this.pollux.stop();

if (notNil(this.pluto.stop)) {
await this.pluto.stop();
}
}

private runTask<T>(task: Task<T>): Promise<T> {
Expand Down
Loading

1 comment on commit 9e459c5

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines Statements Branches Functions
Coverage: 77%
78.09% (3740/4789) 68.51% (1726/2519) 81.95% (890/1086)

Please sign in to comment.