Skip to content

Commit

Permalink
feat(core): basic record to mark restore as complete (#868)
Browse files Browse the repository at this point in the history
* feat: basic record to mark restore as complete

* feat: mark done when finished recovering in the DB

* feat: update unittest

* update: resolve comment

* update: update unit test

* feat: resolve comment

* feat: resolve comment

* feat: resolve comment

* feat: call loadDatabase() after syncWithKeria and before startup app

* feat: remove unused import
  • Loading branch information
Sotatek-TungNguyen2a authored Dec 19, 2024
1 parent 605c48a commit b3cadb8
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 23 deletions.
21 changes: 20 additions & 1 deletion src/core/agent/agent.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SignifyClient, ready as signifyReady, Tier } from "signify-ts";
import { mnemonicToEntropy } from "bip39";
import { AgentUrls } from "./agent.types";
import { AgentUrls, MiscRecordId } from "./agent.types";
import { Agent } from "./agent";
import { KeyStoreKeys, SecureStorage } from "../storage";
import { CoreEventEmitter } from "./event";
Expand Down Expand Up @@ -28,15 +28,21 @@ const getKeyStoreSpy = jest
.mockResolvedValue(mockGetBranValue);
const mockBasicStorageService = {
save: jest.fn(),
update: jest.fn(),
};

const mockConnectionService = {
removeConnectionsPendingDeletion: jest.fn(),
resolvePendingConnections: jest.fn(),
syncKeriaContacts: jest.fn(),
};
const mockIdentifierService = {
resolvePendingIdentifier: jest.fn(),
removeIdentifiersPendingDeletion: jest.fn(),
syncKeriaIdentifiers: jest.fn(),
};
const mockCredentialService = {
syncACDCs: jest.fn(),
};

const mockEntropy = "00000000000000000000000000000000";
Expand All @@ -57,6 +63,7 @@ describe("test cases of bootAndConnect function", () => {
(agent as any).agentServicesProps = mockAgentServicesProps;
(agent as any).connectionService = mockConnectionService;
(agent as any).identifierService = mockIdentifierService;
(agent as any).credentialService = mockCredentialService;

mockAgentUrls = {
url: "http://127.0.0.1:3901",
Expand Down Expand Up @@ -285,12 +292,24 @@ describe("test cases of recoverKeriaAgent function", () => {

await agent.recoverKeriaAgent(mockSeedPhrase, mockConnectUrl);

const now = new Date();
expect(SignifyClient).toHaveBeenCalledWith(
mockConnectUrl,
expectedBran,
Tier.low
);
expect(mockConnectionService.syncKeriaContacts).toHaveBeenCalled();
expect(mockIdentifierService.syncKeriaIdentifiers).toHaveBeenCalled();
expect(mockCredentialService.syncACDCs).toHaveBeenCalled();
expect(mockSignifyClient.connect).toHaveBeenCalled();
expect(mockBasicStorageService.update).toHaveBeenCalledWith({
_tags: {},
content: { syncing: false },
createdAt: now,
id: MiscRecordId.CLOUD_RECOVERY_STATUS,
type: "BasicRecord",
updatedAt: undefined,
});
expect(SecureStorage.set).toHaveBeenCalledWith(
KeyStoreKeys.SIGNIFY_BRAN,
expectedBran
Expand Down
20 changes: 20 additions & 0 deletions src/core/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,32 @@ class Agent {
this.agentServicesProps.signifyClient = this.signifyClient;
await this.connectSignifyClient();

await this.basicStorage.save({
id: MiscRecordId.CLOUD_RECOVERY_STATUS,
content: { syncing: true },
});

await SecureStorage.set(KeyStoreKeys.SIGNIFY_BRAN, bran);
await this.saveAgentUrls({
url: connectUrl,
bootUrl: "",
});

await this.syncWithKeria();
}

async syncWithKeria() {
this.markAgentStatus(true);
await this.connections.syncKeriaContacts();
await this.identifiers.syncKeriaIdentifiers();
await this.credentials.syncACDCs();

await this.basicStorage.update(
new BasicRecord({
id: MiscRecordId.CLOUD_RECOVERY_STATUS,
content: { syncing: false },
})
);
}

private async connectSignifyClient(): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions src/core/agent/agent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum MiscRecordId {
LOGIN_METADATA = "login-metadata",
CAMERA_DIRECTION = "camera-direction",
FAILED_NOTIFICATIONS = "failed-notifications",
CLOUD_RECOVERY_STATUS = "cloud-recovery-status",
IDENTIFIERS_PENDING_CREATION = "identifiers-pending-creation",
}

Expand Down
10 changes: 10 additions & 0 deletions src/core/agent/event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CredentialShortDetails,
CredentialStatus,
} from "./services/credentialService.types";
import { IdentifierShortDetails } from "./services/identifier.types";

interface BaseEventEmitter {
type: string;
Expand All @@ -22,6 +23,7 @@ enum EventTypes {
KeriaStatusChanged = "KeriaStatusChanged",
NotificationRemoved = "NotificationRemoved",
IdentifierRemoved = "IdentifierRemoved",
IdentifierStateChanged = "IdentifierStateChanged",
IdentifierAdded = "IdentifierAdded",
}

Expand Down Expand Up @@ -94,6 +96,13 @@ interface IdentifierRemovedEvent extends BaseEventEmitter {
};
}

interface IdentifierStateChangedEvent extends BaseEventEmitter {
type: typeof EventTypes.IdentifierStateChanged;
payload: {
identifier: IdentifierShortDetails;
};
}

interface IdentifierAddedEvent extends BaseEventEmitter {
type: typeof EventTypes.IdentifierAdded;
payload: {
Expand All @@ -112,6 +121,7 @@ export type {
NotificationRemovedEvent,
ConnectionRemovedEvent,
IdentifierRemovedEvent,
IdentifierStateChangedEvent,
IdentifierAddedEvent,
};
export { EventTypes };
2 changes: 2 additions & 0 deletions src/core/agent/services/connectionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ describe("Connection service of agent", () => {
wellKnowns: [],
},
]);

eventEmitter.emit = jest.fn();
connectionStorage.getAll = jest.fn().mockReturnValue([]);
await connectionService.syncKeriaContacts();
expect(connectionStorage.save).toBeCalledTimes(2);
Expand Down
11 changes: 0 additions & 11 deletions src/core/agent/services/connectionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,6 @@ class ConnectionService extends AgentService {
}
await this.createConnectionMetadata(connectionId, connectionMetadata);
} else {
this.props.eventEmitter.emit<ConnectionStateChangedEvent>({
type: EventTypes.ConnectionStateChanged,
payload: {
isMultiSigInvite: false,
connectionId,
status: ConnectionStatus.PENDING,
url,
label: alias,
},
});

await this.createConnectionMetadata(connectionId, connectionMetadata);
}
return { type: KeriConnectionType.NORMAL, connection };
Expand Down
4 changes: 3 additions & 1 deletion src/core/agent/services/credentialService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ const credentialStorage = jest.mocked({
updateCredentialMetadata: jest.fn(),
});

const eventEmitter = new CoreEventEmitter();
const agentServicesProps = {
signifyClient: signifyClient as any,
eventEmitter: new CoreEventEmitter(),
eventEmitter,
};

const notificationStorage = jest.mocked({
Expand Down Expand Up @@ -422,6 +423,7 @@ describe("Credential service of agent", () => {
...memberIdentifierRecord,
id: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl",
});
eventEmitter.emit = jest.fn();
await credentialService.syncACDCs();
expect(credentialStorage.saveCredentialMetadataRecord).toBeCalledTimes(2);

Expand Down
6 changes: 4 additions & 2 deletions src/core/agent/services/credentialService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class CredentialService extends AgentService {
const identifier = await this.identifierStorage.getIdentifierMetadata(
credential.sad.a.i
);
await this.createMetadata({
const metadata = {
id: credential.sad.d,
isArchived: false,
issuanceDate: new Date(credential.sad.a.dt).toISOString(),
Expand All @@ -193,7 +193,9 @@ class CredentialService extends AgentService {
? IdentifierType.Group
: IdentifierType.Individual,
createdAt: new Date(credential.sad.a.dt),
});
};

await this.createMetadata(metadata);
} catch (error) {
/* eslint-disable no-console */
console.error(error);
Expand Down
3 changes: 1 addition & 2 deletions src/core/agent/services/identifierService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,6 @@ describe("Single sig service of agent", () => {
jest
.spyOn(signifyClient.operations(), "get")
.mockResolvedValue(mockOperation);

getIdentifiersMock.mockResolvedValue({
icp_dt: "2024-12-10T07:28:18.217384+00:00",
});
Expand Down Expand Up @@ -903,7 +902,7 @@ describe("Single sig service of agent", () => {
identifierStorage.createIdentifierMetadataRecord
).toHaveBeenCalledWith({
id: "EJ9oenRW3_SNc0JkETnOegspNGaDCypBfTU1kJiL2AMs",
displayName: "EJ9oenRW3_SNc0JkETnOegspNGaDCypBfTU1kJiL2AMs",
displayName: "test2",
theme: 33,
isPending: false,
createdAt: new Date("2024-12-10T07:28:18.217384+00:00"),
Expand Down
15 changes: 10 additions & 5 deletions src/core/agent/services/identifierService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
EventTypes,
IdentifierAddedEvent,
IdentifierRemovedEvent,
IdentifierStateChangedEvent,
OperationAddedEvent,
} from "../event.types";

Expand Down Expand Up @@ -81,6 +82,12 @@ class IdentifierService extends AgentService {
);
}

onIdentifierStateChanged(
callback: (event: IdentifierStateChangedEvent) => void
) {
this.props.eventEmitter.on(EventTypes.IdentifierStateChanged, callback);
}

onIdentifierAdded(callback: (event: IdentifierAddedEvent) => void) {
this.props.eventEmitter.on(EventTypes.IdentifierAdded, callback);
}
Expand Down Expand Up @@ -521,15 +528,14 @@ class IdentifierService extends AgentService {
const isMultiSig = name.length === 3;
const identifierDetail = (await this.props.signifyClient
.identifiers()
.get(identifier)) as HabState & { icp_dt: string };

.get(identifier.prefix)) as HabState & { icp_dt: string };
if (isMultiSig) {
const groupId = identifier.name.split(":")[1];
const groupInitiator = groupId.split("-")[0] === "1";

await this.identifierStorage.createIdentifierMetadataRecord({
id: identifier.prefix,
displayName: groupId,
displayName: identifier.name.split(":")[1],
theme,
groupMetadata: {
groupId,
Expand All @@ -539,13 +545,12 @@ class IdentifierService extends AgentService {
isPending,
createdAt: new Date(identifierDetail.icp_dt),
});

continue;
}

await this.identifierStorage.createIdentifierMetadataRecord({
id: identifier.prefix,
displayName: identifier.prefix,
displayName: identifier.name.split(":")[1],
theme,
isPending,
createdAt: new Date(identifierDetail.icp_dt),
Expand Down
11 changes: 10 additions & 1 deletion src/ui/components/AppWrapper/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import {
import {
AcdcStateChangedEvent,
ConnectionStateChangedEvent,
IdentifierStateChangedEvent,
} from "../../../core/agent/event.types";
import { IdentifiersFilters } from "../../pages/Identifiers/Identifiers.types";
import { CredentialsFilters } from "../../pages/Credentials/Credentials.types";
Expand Down Expand Up @@ -543,14 +544,22 @@ const AppWrapper = (props: { children: ReactNode }) => {
await Agent.agent.devPreload();
}

await loadDatabase();
const { keriaConnectUrlRecord } = await loadCacheBasicStorage();

// Ensure online/offline callback setup before connecting to KERIA
setupEventServiceCallbacks();

if (keriaConnectUrlRecord) {
try {
if (keriaConnectUrlRecord?.content?.url) {
const recoveryStatus = await Agent.agent.basicStorage.findById(
MiscRecordId.CLOUD_RECOVERY_STATUS
);
if (recoveryStatus?.content?.syncing) {
await Agent.agent.syncWithKeria();
}
}
await loadDatabase();
await Agent.agent.start(keriaConnectUrlRecord.content.url as string);
} catch (e) {
const errorMessage = (e as Error).message;
Expand Down

0 comments on commit b3cadb8

Please sign in to comment.