diff --git a/src/core/__fixtures__/agent/keriaNotificationFixtures.ts b/src/core/__fixtures__/agent/keriaNotificationFixtures.ts index 0fa328d19..ca8e18083 100644 --- a/src/core/__fixtures__/agent/keriaNotificationFixtures.ts +++ b/src/core/__fixtures__/agent/keriaNotificationFixtures.ts @@ -298,7 +298,7 @@ const notificationIpexGrantProp = { const notificationIpexAgreeProp = { i: "string", - dt: "string", + dt: "2024-12-10T07:28:18.217384+00:00", r: false, a: { r: NotificationRoute.ExnIpexAgree, diff --git a/src/core/__fixtures__/agent/multSigFixtures.ts b/src/core/__fixtures__/agent/multSigFixtures.ts index b3722cf91..dbb32b907 100644 --- a/src/core/__fixtures__/agent/multSigFixtures.ts +++ b/src/core/__fixtures__/agent/multSigFixtures.ts @@ -58,6 +58,7 @@ const getMemberIdentifierResponse = { }, di: "", }, + icp_dt: "2024-08-09T07:23:52.839894+00:00" }; const getMultisigIdentifierResponse = { diff --git a/src/core/agent/services/credentialService.test.ts b/src/core/agent/services/credentialService.test.ts index f88786ecc..fb942c3df 100644 --- a/src/core/agent/services/credentialService.test.ts +++ b/src/core/agent/services/credentialService.test.ts @@ -394,7 +394,7 @@ describe("Credential service of agent", () => { }, schema: { $id: "id-1", - tile: "title1", + title: "title1", }, }, { @@ -413,7 +413,7 @@ describe("Credential service of agent", () => { }, schema: { $id: "id-2", - tile: "title2", + title: "title2", }, }, ]); @@ -424,6 +424,35 @@ describe("Credential service of agent", () => { }); await credentialService.syncACDCs(); expect(credentialStorage.saveCredentialMetadataRecord).toBeCalledTimes(2); + + expect(credentialStorage.saveCredentialMetadataRecord).toHaveBeenCalledWith( + expect.objectContaining({ + id: "EIuZp_JvO5pbNmS8jyG96t3d4XENaFSJpLtbLySxvz-X", + isArchived: false, + issuanceDate: "2023-11-29T02:13:34.858Z", + credentialType: "title1", + status: CredentialStatus.CONFIRMED, + connectionId: "ECTcHGs3EhJEdVTW10vm5pkiDlOXlR8bPBj9-8LSpZ3W", + schema: "id-1", + identifierId: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl", + identifierType: IdentifierType.Individual, + createdAt: new Date("2023-11-29T02:13:34.858Z"), + })); + + expect(credentialStorage.saveCredentialMetadataRecord).toHaveBeenCalledWith( + expect.objectContaining({ + id: "EL24R3ECGtv_UzQmYUcu9AeP1ks2JPzTxgPcQPkadEPY", + isArchived: false, + issuanceDate: "2023-11-29T02:12:35.716Z", + credentialType: "title2", + status: CredentialStatus.CONFIRMED, + connectionId: "ECTcHGs3EhJEdVTW10vm5pkiDlOXlR8bPBj9-8LSpZ3W", + schema: "id-2", + identifierId: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl", + identifierType: IdentifierType.Individual, + createdAt: new Date("2023-11-29T02:12:35.716Z"), + }) + ); }); test("Must throw 'Credential with given SAID not found on KERIA' when there's no KERI credential", async () => { diff --git a/src/core/agent/services/credentialService.ts b/src/core/agent/services/credentialService.ts index 939b299ca..1a7df5e2b 100644 --- a/src/core/agent/services/credentialService.ts +++ b/src/core/agent/services/credentialService.ts @@ -192,6 +192,7 @@ class CredentialService extends AgentService { identifierType: identifier.multisigManageAid ? IdentifierType.Group : IdentifierType.Individual, + createdAt: new Date(credential.sad.a.dt), }); } catch (error) { /* eslint-disable no-console */ diff --git a/src/core/agent/services/identifier.types.ts b/src/core/agent/services/identifier.types.ts index 38e6a2808..96e8ac6aa 100644 --- a/src/core/agent/services/identifier.types.ts +++ b/src/core/agent/services/identifier.types.ts @@ -45,6 +45,7 @@ interface MultiSigIcpRequestDetails { interface CreateIdentifierResult { identifier: string; isPending: boolean; + createdAt: string; } enum IdentifierType { diff --git a/src/core/agent/services/identifierService.test.ts b/src/core/agent/services/identifierService.test.ts index a327f8448..154e26045 100644 --- a/src/core/agent/services/identifierService.test.ts +++ b/src/core/agent/services/identifierService.test.ts @@ -360,6 +360,8 @@ describe("Single sig service of agent", () => { }), }); + getIdentifiersMock.mockResolvedValue(groupIdentifierStateKeria); + expect( await identifierService.createIdentifier({ displayName, @@ -404,6 +406,7 @@ describe("Single sig service of agent", () => { id: "op123", recordType: OperationPendingRecordType.Witness, }); + getIdentifiersMock.mockResolvedValue(groupIdentifierStateKeria); expect( await identifierService.createIdentifier({ @@ -680,6 +683,10 @@ 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" + }); // Call the function to test await identifierService.syncKeriaIdentifiers(); @@ -697,6 +704,7 @@ describe("Single sig service of agent", () => { groupInitiator: true, }, isPending: false, + createdAt: new Date("2024-12-10T07:28:18.217384+00:00") }); expect( @@ -706,6 +714,7 @@ describe("Single sig service of agent", () => { displayName: "EJ9oenRW3_SNc0JkETnOegspNGaDCypBfTU1kJiL2AMs", theme: 33, isPending: false, + createdAt: new Date("2024-12-10T07:28:18.217384+00:00") }); // sync data of group record @@ -728,6 +737,7 @@ describe("Single sig service of agent", () => { theme: 15, multisigManageAid: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl", isPending: false, + createdAt: new Date("2024-12-10T07:28:18.217384+00:00") }); }); diff --git a/src/core/agent/services/identifierService.ts b/src/core/agent/services/identifierService.ts index 7bf10180d..c204cf990 100644 --- a/src/core/agent/services/identifierService.ts +++ b/src/core/agent/services/identifierService.ts @@ -184,6 +184,10 @@ class IdentifierService extends AgentService { throw error; }); const identifier = operation.serder.ked.i; + + // @TODO - foconnor: Need update HabState interface on signify. + const identifierDetail = await this.props.signifyClient.identifiers().get(identifier) as HabState & { icp_dt: string }; + const addRoleOperation = await this.props.signifyClient .identifiers() .addEndRole(identifier, "agent", this.props.signifyClient.agent!.pre); @@ -211,8 +215,9 @@ class IdentifierService extends AgentService { id: identifier, ...metadata, isPending: !op.done, + createdAt: new Date(identifierDetail.icp_dt) }); - return { identifier, isPending: !op.done }; + return { identifier, isPending: !op.done, createdAt: identifierDetail.icp_dt }; } async deleteIdentifier(identifier: string): Promise { @@ -393,6 +398,7 @@ class IdentifierService extends AgentService { const name = identifier.name.split(":"); const theme = parseInt(name[0], 10); const isMultiSig = name.length === 3; + const identifierDetail = await this.props.signifyClient.identifiers().get(identifier) as HabState & { icp_dt: string }; if (isMultiSig) { const groupId = identifier.name.split(":")[1]; @@ -408,6 +414,7 @@ class IdentifierService extends AgentService { groupInitiator, }, isPending, + createdAt: new Date(identifierDetail.icp_dt) }); continue; @@ -418,6 +425,7 @@ class IdentifierService extends AgentService { displayName: identifier.prefix, theme, isPending, + createdAt: new Date(identifierDetail.icp_dt) }); } @@ -434,6 +442,7 @@ class IdentifierService extends AgentService { .operations() .get(`group.${identifier.prefix}`); const isPending = !op.done; + const identifierDetail = await this.props.signifyClient.identifiers().get(identifier) as HabState & { icp_dt: string }; if (isPending) { const pendingOperation = await this.operationPendingStorage.save({ @@ -460,7 +469,9 @@ class IdentifierService extends AgentService { theme, multisigManageAid, isPending, - }); + createdAt: new Date(identifierDetail.icp_dt + ) + }) } } diff --git a/src/core/agent/services/ipexCommunicationService.test.ts b/src/core/agent/services/ipexCommunicationService.test.ts index d03095539..83c632049 100644 --- a/src/core/agent/services/ipexCommunicationService.test.ts +++ b/src/core/agent/services/ipexCommunicationService.test.ts @@ -354,6 +354,7 @@ describe("Receive individual ACDC actions", () => { ...credentialRecordProps, identifierId: "identifierId", identifierType: "individual", + createdAt: new Date(credentialRecordProps.issuanceDate) }); expect(eventEmitter.emit).toHaveBeenCalledWith({ type: EventTypes.AcdcStateChanged, @@ -362,6 +363,7 @@ describe("Receive individual ACDC actions", () => { ...credentialRecordProps, identifierId: "identifierId", identifierType: "individual", + createdAt: new Date(credentialRecordProps.issuanceDate) }, status: CredentialStatus.PENDING, }, @@ -551,6 +553,7 @@ describe("Receive group ACDC actions", () => { ...credentialRecordProps, identifierId: "EC1cyV3zLnGs4B9AYgoGNjXESyQZrBWygz3jLlRD30bR", identifierType: "group", + createdAt: new Date(credentialRecordProps.issuanceDate) }); expect(eventEmitter.emit).toHaveBeenCalledWith({ type: EventTypes.AcdcStateChanged, @@ -559,6 +562,7 @@ describe("Receive group ACDC actions", () => { ...credentialRecordProps, identifierId: "EC1cyV3zLnGs4B9AYgoGNjXESyQZrBWygz3jLlRD30bR", identifierType: "group", + createdAt: new Date(credentialRecordProps.issuanceDate) }, status: CredentialStatus.PENDING, }, @@ -744,6 +748,7 @@ describe("Receive group ACDC actions", () => { ...credentialRecordProps, identifierId: "EC1cyV3zLnGs4B9AYgoGNjXESyQZrBWygz3jLlRD30bR", identifierType: "group", + createdAt: new Date(credentialRecordProps.issuanceDate) }); expect(eventEmitter.emit).toHaveBeenCalledWith({ type: EventTypes.AcdcStateChanged, @@ -752,6 +757,7 @@ describe("Receive group ACDC actions", () => { ...credentialRecordProps, identifierId: "EC1cyV3zLnGs4B9AYgoGNjXESyQZrBWygz3jLlRD30bR", identifierType: "group", + createdAt: new Date(credentialRecordProps.issuanceDate) }, status: CredentialStatus.PENDING, }, diff --git a/src/core/agent/services/ipexCommunicationService.ts b/src/core/agent/services/ipexCommunicationService.ts index ebdeef0e6..13620230b 100644 --- a/src/core/agent/services/ipexCommunicationService.ts +++ b/src/core/agent/services/ipexCommunicationService.ts @@ -443,6 +443,7 @@ class IpexCommunicationService extends AgentService { identifierType: holder.multisigManageAid ? IdentifierType.Group : IdentifierType.Individual, + createdAt: new Date(dateTime), }; await this.credentialStorage.saveCredentialMetadataRecord( credentialDetails diff --git a/src/core/agent/services/keriaNotificationService.test.ts b/src/core/agent/services/keriaNotificationService.test.ts index 9bff99baa..9f2e8cd33 100644 --- a/src/core/agent/services/keriaNotificationService.test.ts +++ b/src/core/agent/services/keriaNotificationService.test.ts @@ -1,3 +1,4 @@ +import { create } from "domain"; import { Agent } from "../agent"; import { ConnectionStatus, @@ -1373,7 +1374,8 @@ describe("Signify notification service of agent", () => { connectionId: "EC9bQGHShmp2Juayqp0C5XcheBiHyc1p54pZ_Op-B95x", credentialId: undefined, id: notificationIpexAgreeProp.i, - route: NotificationRoute.ExnIpexAgree + route: NotificationRoute.ExnIpexAgree, + createdAt: new Date(notificationIpexAgreeProp.dt) }); expect(eventEmitter.emit).not.toBeCalled(); expect(ipexCommunications.grantAcdcFromAgree).toBeCalledWith("string"); @@ -1402,7 +1404,8 @@ describe("Signify notification service of agent", () => { connectionId: "EC9bQGHShmp2Juayqp0C5XcheBiHyc1p54pZ_Op-B95x", credentialId: undefined, id: notificationIpexAgreeProp.i, - route: NotificationRoute.ExnIpexAgree + route: NotificationRoute.ExnIpexAgree, + createdAt: new Date(notificationIpexAgreeProp.dt) }); expect(eventEmitter.emit).not.toBeCalled(); expect(ipexCommunications.grantAcdcFromAgree).toBeCalledWith("string"); @@ -1574,7 +1577,8 @@ describe("Group IPEX presentation", () => { connectionId: "EC9bQGHShmp2Juayqp0C5XcheBiHyc1p54pZ_Op-B95x", credentialId: undefined, id: notificationIpexAgreeProp.i, - route: NotificationRoute.ExnIpexAgree + route: NotificationRoute.ExnIpexAgree, + createdAt: new Date(notificationIpexAgreeProp.dt) }); expect(eventEmitter.emit).not.toBeCalled(); expect(identifiersMemberMock).toBeCalledWith("EBEWfIUOn789yJiNRnvKqpbWE3-m6fSDxtu6wggybbli"); @@ -1617,7 +1621,8 @@ describe("Group IPEX presentation", () => { connectionId: "EC9bQGHShmp2Juayqp0C5XcheBiHyc1p54pZ_Op-B95x", credentialId: undefined, id: notificationIpexAgreeProp.i, - route: NotificationRoute.ExnIpexAgree + route: NotificationRoute.ExnIpexAgree, + createdAt: new Date(notificationIpexAgreeProp.dt) }); expect(eventEmitter.emit).not.toBeCalled(); expect(identifiersMemberMock).toBeCalledWith("EBEWfIUOn789yJiNRnvKqpbWE3-m6fSDxtu6wggybbli"); diff --git a/src/core/agent/services/keriaNotificationService.ts b/src/core/agent/services/keriaNotificationService.ts index 9c2dc74da..fe0a26667 100644 --- a/src/core/agent/services/keriaNotificationService.ts +++ b/src/core/agent/services/keriaNotificationService.ts @@ -737,6 +737,7 @@ class KeriaNotificationService extends AgentService { read: false, route: event.a.r as NotificationRoute, connectionId: exchange.exn.i, + createdAt: new Date(event.dt), credentialId: exchange.exn.e?.acdc?.d, }; diff --git a/src/core/agent/services/multiSigService.test.ts b/src/core/agent/services/multiSigService.test.ts index 0d68a0109..62a781323 100644 --- a/src/core/agent/services/multiSigService.test.ts +++ b/src/core/agent/services/multiSigService.test.ts @@ -220,6 +220,17 @@ describe("Oobi/endrole", () => { ); expect(addEndRoleMock).toBeCalledTimes(1); (memberMetadataRecord.groupMetadata as any).groupCreated = false; + + expect(identifierStorage.createIdentifierMetadataRecord).toBeCalledWith( + { + id: "newMultisigIdentifierAid", + displayName: "Identifier 2", + theme: 0, + isPending: false, + multisigManageAid: "creatorIdentifier", + createdAt: new Date("2024-08-09T07:23:52.839894+00:00") + } + ) }); test("Can add end role authorization", async () => { @@ -406,7 +417,14 @@ describe("Creation of multi-sig", () => { isPending: true, }); expect(identifierStorage.createIdentifierMetadataRecord).toBeCalledWith( - expect.objectContaining({ id: multisigIdentifier, isPending: true }) + expect.objectContaining({ + id: multisigIdentifier, + displayName: "Identifier 2", + theme: 0, + isPending: true, + multisigManageAid: "creatorIdentifier", + createdAt: new Date("2024-08-09T07:23:52.839Z") + }) ); expect(eventEmitter.emit).toHaveBeenCalledWith({ @@ -446,6 +464,11 @@ describe("Creation of multi-sig", () => { expect(identifierStorage.createIdentifierMetadataRecord).toBeCalledWith( expect.objectContaining({ id: `${multisigIdentifier}1`, + displayName: "Identifier 2", + theme: 0, + isPending: true, + multisigManageAid: "creatorIdentifier", + createdAt: new Date("2024-08-09T07:23:52.839Z") }) ); @@ -477,6 +500,11 @@ describe("Creation of multi-sig", () => { expect(identifierStorage.createIdentifierMetadataRecord).toBeCalledWith( expect.objectContaining({ id: `${multisigIdentifier}2`, + displayName: "Identifier 2", + theme: 0, + isPending: true, + multisigManageAid: "creatorIdentifier", + createdAt: new Date("2024-08-09T07:23:52.839Z") }) ); diff --git a/src/core/agent/services/multiSigService.ts b/src/core/agent/services/multiSigService.ts index 7fcc8ee8f..f1ba42249 100644 --- a/src/core/agent/services/multiSigService.ts +++ b/src/core/agent/services/multiSigService.ts @@ -135,12 +135,17 @@ class MultiSigService extends AgentService { const multisigId = op.name.split(".")[1]; const isPending = !op.done; + const multisigDetail = await this.props.signifyClient + .identifiers() + .get(multisigId as string) as HabState & { icp_dt: string }; + await this.identifierStorage.createIdentifierMetadataRecord({ id: multisigId, displayName: ourMetadata.displayName, theme: ourMetadata.theme, isPending, multisigManageAid: ourIdentifier, + createdAt: new Date(multisigDetail.icp_dt) }); ourMetadata.groupMetadata.groupCreated = true; await this.identifierStorage.updateIdentifierMetadata( @@ -368,13 +373,17 @@ class MultiSigService extends AgentService { const op = res.op; const multisigId = op.name.split(".")[1]; const isPending = !op.done; - + const multisigDetail = await this.props.signifyClient + .identifiers() + .get(multisigId) as HabState & { icp_dt: string }; + await this.identifierStorage.createIdentifierMetadataRecord({ id: multisigId, displayName: meta.displayName, theme: meta.theme, isPending, multisigManageAid: identifier.id, + createdAt: new Date(multisigDetail.icp_dt) }); identifier.groupMetadata.groupCreated = true; await this.identifierStorage.updateIdentifierMetadata( diff --git a/src/ui/components/CreateIdentifier/CreateIdentifier.tsx b/src/ui/components/CreateIdentifier/CreateIdentifier.tsx index db6accf00..f6e033f13 100644 --- a/src/ui/components/CreateIdentifier/CreateIdentifier.tsx +++ b/src/ui/components/CreateIdentifier/CreateIdentifier.tsx @@ -138,7 +138,7 @@ const CreateIdentifier = ({ } metadata.groupMetadata = groupMetadata; try { - const { identifier, isPending } = + const { identifier, isPending, createdAt } = await Agent.agent.identifiers.createIdentifier(metadata); if(!identifier) { @@ -148,7 +148,7 @@ const CreateIdentifier = ({ const newIdentifier: IdentifierShortDetails = { id: identifier, displayName: identifierData.displayName, - createdAtUTC: new Date().toISOString(), + createdAtUTC: createdAt, theme: selectedTheme, isPending: isPending, };