Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): basic record to mark restore as complete #888

Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c8a68d4
feat: basic record to mark restore as complete
Sotatek-TungNguyen2a Dec 13, 2024
7e210dc
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
Sotatek-TungNguyen2a Dec 13, 2024
b7d4337
feat: mark done when finished recovering in the DB
Sotatek-TungNguyen2a Dec 13, 2024
9bca87c
feat: update unittest
Sotatek-TungNguyen2a Dec 13, 2024
14fe07d
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
Sotatek-TungNguyen2a Dec 16, 2024
b88048a
update: resolve comment
Sotatek-TungNguyen2a Dec 16, 2024
d6262a9
update: update unit test
Sotatek-TungNguyen2a Dec 16, 2024
db6fb03
feat: resolve comment
Sotatek-TungNguyen2a Dec 18, 2024
8dccd3f
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
Sotatek-TungNguyen2a Dec 19, 2024
fd8b348
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
Sotatek-TungNguyen2a Dec 19, 2024
d382f5e
feat: resolve comment
Sotatek-TungNguyen2a Dec 19, 2024
17411f0
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
Sotatek-TungNguyen2a Dec 19, 2024
7575554
feat: resolve comment
Sotatek-TungNguyen2a Dec 19, 2024
200fe37
feat: call loadDatabase() after syncWithKeria and before startup app
Sotatek-TungNguyen2a Dec 19, 2024
e1f500a
feat: remove unused import
Sotatek-TungNguyen2a Dec 19, 2024
e65967f
Merge branch 'develop' into feat/DTIS-1540-basic-record-to-mark-resto…
Sotatek-TungNguyen2a Dec 30, 2024
0223f74
update: remove unnecessary event references and resolve comment
Sotatek-TungNguyen2a Dec 30, 2024
21250b5
update: separate to set agent to online in AppWrapper
Sotatek-TungNguyen2a Dec 31, 2024
3505610
update: remove decorator check online when syncing keria
Sotatek-TungNguyen2a Dec 31, 2024
f9314f8
update: add the pagination for sync methods
Sotatek-TungNguyen2a Dec 31, 2024
976c2f7
fix(core): fix connection handling and pagination while syncing ident…
iFergal Jan 2, 2025
684d6e5
Merge remote-tracking branch 'origin/develop' into feat/DTIS-1540-bas…
iFergal Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/core/agent/agent.test.ts
Original file line number Diff line number Diff line change
@@ -23,12 +23,13 @@ const mockAgentServicesProps = {
};

const mockGetBranValue = "AEsI_2YqNsQlf8brzDJaP";
const getKeyStoreSpy = jest
jest
.spyOn(SecureStorage, "get")
.mockResolvedValue(mockGetBranValue);
const mockBasicStorageService = {
save: jest.fn(),
update: jest.fn(),
createOrUpdateBasicRecord: jest.fn(),
};

const mockConnectionService = {
@@ -42,7 +43,7 @@ const mockIdentifierService = {
syncKeriaIdentifiers: jest.fn(),
};
const mockCredentialService = {
syncACDCs: jest.fn(),
syncKeriaCredentials: jest.fn(),
};

const mockEntropy = "00000000000000000000000000000000";
@@ -300,9 +301,9 @@ describe("test cases of recoverKeriaAgent function", () => {
);
expect(mockConnectionService.syncKeriaContacts).toHaveBeenCalled();
expect(mockIdentifierService.syncKeriaIdentifiers).toHaveBeenCalled();
expect(mockCredentialService.syncACDCs).toHaveBeenCalled();
expect(mockCredentialService.syncKeriaCredentials).toHaveBeenCalled();
expect(mockSignifyClient.connect).toHaveBeenCalled();
expect(mockBasicStorageService.update).toHaveBeenCalledWith({
expect(mockBasicStorageService.createOrUpdateBasicRecord).toHaveBeenCalledWith({
_tags: {},
content: { syncing: false },
createdAt: now,
8 changes: 4 additions & 4 deletions src/core/agent/agent.ts
Original file line number Diff line number Diff line change
@@ -228,7 +228,6 @@ class Agent {
this.signifyClient = new SignifyClient(keriaConnectUrl, bran, Tier.low);
this.agentServicesProps.signifyClient = this.signifyClient;
await this.connectSignifyClient();
this.markAgentStatus(true);
}
}

@@ -312,17 +311,18 @@ class Agent {
}

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

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

this.markAgentStatus(true);
}

private async connectSignifyClient(): Promise<void> {
4 changes: 0 additions & 4 deletions src/core/agent/services/connectionService.test.ts
Original file line number Diff line number Diff line change
@@ -518,7 +518,6 @@ describe("Connection service of agent", () => {
});

test("Should call createIdentifierMetadataRecord when there are un-synced KERI contacts", async () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValueOnce(true);
contactListMock.mockReturnValue([
{
id: "EBaDnyriYK_FAruigHO42avVN40fOlVSUxpxXJ1fNxFR",
@@ -632,9 +631,6 @@ describe("Connection service of agent", () => {
await expect(
connectionService.getConnectionById("id")
).rejects.toThrowError(Agent.KERIA_CONNECTION_BROKEN);
await expect(connectionService.syncKeriaContacts()).rejects.toThrowError(
Agent.KERIA_CONNECTION_BROKEN
);
await expect(
connectionService.deleteConnectionById("id")
).rejects.toThrowError(Agent.KERIA_CONNECTION_BROKEN);
47 changes: 28 additions & 19 deletions src/core/agent/services/connectionService.ts
Original file line number Diff line number Diff line change
@@ -166,12 +166,23 @@ class ConnectionService extends AgentService {
connection,
};
}
} else {
await this.resolveOobi(url, multiSigInvite);
connectionMetadata.pending = false;
connectionMetadata.status = ConnectionStatus.CONFIRMED;
}

await this.createConnectionMetadata(connectionId, connectionMetadata);

if (!multiSigInvite) {
this.props.eventEmitter.emit<ConnectionStateChangedEvent>({
type: EventTypes.ConnectionStateChanged,
payload: {
isMultiSigInvite: false,
connectionId,
status: ConnectionStatus.PENDING,
url,
label: alias,
},
});
}

return { type: KeriConnectionType.NORMAL, connection };
}

@@ -418,24 +429,22 @@ class ConnectionService extends AgentService {
}

// @TODO - foconnor: Contacts that are smid/rmids for multisigs will be synced too.
@OnlineOnly
async syncKeriaContacts() {
const signifyContacts = await this.props.signifyClient.contacts().list();
const storageContacts = await this.connectionStorage.getAll();
const unSyncedData = signifyContacts.filter(
const cloudContacts = await this.props.signifyClient.contacts().list();
const localContacts = await this.connectionStorage.getAll();

const unSyncedData = cloudContacts.filter(
(contact: Contact) =>
!storageContacts.find((item: ConnectionRecord) => contact.id == item.id)
!localContacts.find((item: ConnectionRecord) => contact.id == item.id)
);
if (unSyncedData.length) {
//sync the storage with the signify data
for (const contact of unSyncedData) {
await this.createConnectionMetadata(contact.id, {
alias: contact.alias,
oobi: contact.oobi,
groupId: contact.groupCreationId,
createdAtUTC: contact.createdAt,
});
}

for (const contact of unSyncedData) {
await this.createConnectionMetadata(contact.id, {
alias: contact.alias,
oobi: contact.oobi,
groupId: contact.groupCreationId,
createdAtUTC: contact.createdAt,
});
}
}

17 changes: 7 additions & 10 deletions src/core/agent/services/credentialService.test.ts
Original file line number Diff line number Diff line change
@@ -372,13 +372,10 @@ describe("Credential service of agent", () => {
await expect(
credentialService.getCredentialDetailsById("not-found-id")
).rejects.toThrowError(Agent.KERIA_CONNECTION_BROKEN);
await expect(credentialService.syncACDCs()).rejects.toThrowError(
Agent.KERIA_CONNECTION_BROKEN
);
});
test("Should call saveCredentialMetadataRecord when there are un-synced KERI credentials", async () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValueOnce(true);
credentialListMock.mockReturnValue([

test("Can sync ACDCs from KERIA to local", async () => {
credentialListMock.mockReturnValueOnce([
{
sad: {
v: "ACDC10JSON000197_",
@@ -417,16 +414,17 @@ describe("Credential service of agent", () => {
title: "title2",
},
},
]);
]).mockResolvedValue([]);
credentialStorage.getAllCredentialMetadata = jest.fn().mockReturnValue([]);
identifierStorage.getIdentifierMetadata = jest.fn().mockResolvedValue({
...memberIdentifierRecord,
id: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl",
});
eventEmitter.emit = jest.fn();
await credentialService.syncACDCs();
expect(credentialStorage.saveCredentialMetadataRecord).toBeCalledTimes(2);

await credentialService.syncKeriaCredentials();

expect(credentialStorage.saveCredentialMetadataRecord).toBeCalledTimes(2);
expect(credentialStorage.saveCredentialMetadataRecord).toHaveBeenCalledWith(
expect.objectContaining({
id: "EIuZp_JvO5pbNmS8jyG96t3d4XENaFSJpLtbLySxvz-X",
@@ -440,7 +438,6 @@ describe("Credential service of agent", () => {
identifierType: IdentifierType.Individual,
createdAt: new Date("2023-11-29T02:13:34.858Z"),
}));

expect(credentialStorage.saveCredentialMetadataRecord).toHaveBeenCalledWith(
expect.objectContaining({
id: "EL24R3ECGtv_UzQmYUcu9AeP1ks2JPzTxgPcQPkadEPY",
77 changes: 41 additions & 36 deletions src/core/agent/services/credentialService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CredentialFilter } from "signify-ts";
import { AgentServicesProps } from "../agent.types";
import { AgentService } from "./agentService";
import { CredentialMetadataRecordProps } from "../records/credentialMetadataRecord.types";
@@ -161,46 +162,50 @@ class CredentialService extends AgentService {
return metadata;
}

@OnlineOnly
async syncACDCs() {
const signifyCredentials = await this.props.signifyClient
.credentials()
.list();
async syncKeriaCredentials() {
const cloudCredentials: any[] = [];
let returned = -1;
let iteration = 0;

const storedCredentials =
while (returned !== 0) {
const result = await this.props.signifyClient.credentials().list({
skip: iteration * 24,
limit: 24 + (iteration * 24)
});
cloudCredentials.push(...result);

returned = result.length;
iteration += 1;
}

const localCredentials =
await this.credentialStorage.getAllCredentialMetadata();
const unSyncedData = signifyCredentials.filter(

const unSyncedData = cloudCredentials.filter(
(credential: any) =>
!storedCredentials.find((item) => credential.sad.d === item.id)
!localCredentials.find((item) => credential.sad.d === item.id)
);
if (unSyncedData.length) {
//sync the storage with the signify data
for (const credential of unSyncedData) {
try {
const identifier = await this.identifierStorage.getIdentifierMetadata(
credential.sad.a.i
);
const metadata = {
id: credential.sad.d,
isArchived: false,
issuanceDate: new Date(credential.sad.a.dt).toISOString(),
credentialType: credential.schema.title,
status: CredentialStatus.CONFIRMED,
connectionId: credential.sad.i,
schema: credential.schema.$id,
identifierId: credential.sad.a.i,
identifierType: identifier.multisigManageAid
? IdentifierType.Group
: IdentifierType.Individual,
createdAt: new Date(credential.sad.a.dt),
};

await this.createMetadata(metadata);
} catch (error) {
/* eslint-disable no-console */
console.error(error);
}
}

for (const credential of unSyncedData) {
const identifier = await this.identifierStorage.getIdentifierMetadata(
credential.sad.a.i
);
const metadata = {
id: credential.sad.d,
isArchived: false,
issuanceDate: new Date(credential.sad.a.dt).toISOString(),
credentialType: credential.schema.title,
status: CredentialStatus.CONFIRMED,
connectionId: credential.sad.i,
schema: credential.schema.$id,
identifierId: credential.sad.a.i,
identifierType: identifier.multisigManageAid
? IdentifierType.Group
: IdentifierType.Individual,
createdAt: new Date(credential.sad.a.dt),
};

await this.createMetadata(metadata);
}
}

7 changes: 2 additions & 5 deletions src/core/agent/services/identifierService.test.ts
Original file line number Diff line number Diff line change
@@ -955,7 +955,7 @@ describe("Single sig service of agent", () => {
Agent.agent.getKeriaOnlineStatus = jest.fn().mockReturnValue(true);

// Mock the list of identifiers returned by signifyClient
listIdentifiersMock.mockReturnValue({
listIdentifiersMock.mockReturnValueOnce({
aids: [
{
name: "0:1-group1:test1",
@@ -976,7 +976,7 @@ describe("Single sig service of agent", () => {
prefix: "EJ9oenRW3_SNc0JkETnOegspNGaDCypBfTU1kJiL2AMs",
},
],
});
}).mockReturnValue({ aids: [] });

// Mock the identifier storage
identifierStorage.getKeriIdentifiersMetadata = jest
@@ -1089,9 +1089,6 @@ describe("Single sig service of agent", () => {
await expect(identifierService.getIdentifier("id")).rejects.toThrowError(
Agent.KERIA_CONNECTION_BROKEN
);
await expect(identifierService.syncKeriaIdentifiers()).rejects.toThrowError(
Agent.KERIA_CONNECTION_BROKEN
);
await expect(identifierService.getSigner("id")).rejects.toThrowError(
Agent.KERIA_CONNECTION_BROKEN
);
27 changes: 18 additions & 9 deletions src/core/agent/services/identifierService.ts
Original file line number Diff line number Diff line change
@@ -463,22 +463,31 @@ class IdentifierService extends AgentService {

const manager = this.props.signifyClient.manager;
if (manager) {
return (await manager.get(aid)).signers[0];
return (manager.get(aid)).signers[0];
} else {
throw new Error(IdentifierService.FAILED_TO_OBTAIN_KEY_MANAGER);
}
}

@OnlineOnly
async syncKeriaIdentifiers() {
const { aids: signifyIdentifiers } = await this.props.signifyClient
.identifiers()
.list();
const storageIdentifiers =
const cloudIdentifiers: any[] = [];
let returned = -1;
let iteration = 0;

while (returned !== 0) {
const result = await this.props.signifyClient.identifiers().list(iteration * (24 + 1), 24 + (iteration * (24 + 1)));
cloudIdentifiers.push(...result.aids);

returned = result.aids.length;
iteration += 1;
}

const localIdentifiers =
await this.identifierStorage.getKeriIdentifiersMetadata();
const unSyncedData = signifyIdentifiers.filter(

const unSyncedData = cloudIdentifiers.filter(
(identifier: IdentifierResult) =>
!storageIdentifiers.find((item) => identifier.prefix === item.id)
!localIdentifiers.find((item) => identifier.prefix === item.id)
);

const [unSyncedDataWithGroup, unSyncedDataWithoutGroup] = [
@@ -565,7 +574,7 @@ class IdentifierService extends AgentService {
const isPending = !op.done;
const identifierDetail = (await this.props.signifyClient
.identifiers()
.get(identifier)) as HabState & { icp_dt: string };
.get(identifier.prefix)) as HabState & { icp_dt: string };

if (isPending) {
const pendingOperation = await this.operationPendingStorage.save({
2 changes: 1 addition & 1 deletion src/ui/App.test.tsx
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ jest.mock("../core/agent/agent", () => ({
isCredentialDone: jest.fn(),
updateMetadataCompleted: jest.fn(),
onAcdcStateChanged: jest.fn(),
syncACDCs: jest.fn(),
syncKeriaCredentials: jest.fn(),
},
messages: {
onBasicMessageStateChanged: jest.fn(),
2 changes: 1 addition & 1 deletion src/ui/components/AppWrapper/AppWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ jest.mock("../../../core/agent/agent", () => ({
isCredentialDone: jest.fn(),
updateMetadataCompleted: jest.fn(),
onAcdcStateChanged: jest.fn(),
syncACDCs: jest.fn(),
syncKeriaCredentials: jest.fn(),
},
messages: {
onBasicMessageStateChanged: jest.fn(),
Loading
Loading