Skip to content

Commit

Permalink
feat: IPEX request to present (apply, offer, agree) (#454)
Browse files Browse the repository at this point in the history
* feat: create bulk storage

* test: add test for new storage

* feat: add full ipex flow

* fix: agent init props

* Revert "fix: agent init props"

This reverts commit f0e2878.

* fix: init agent props

* Revert "fix: init agent props"

This reverts commit 06520a4.

* fix: init storage level

* refactor: refactor some field name and code

* refactor: full ipex flow

* fix: fix error filter field with aid grant msg

* refactor: remove some unused stuffs and unit test

* refactor: bulk refactor ipex flow

* fix: get exn with offer and bulk issues
  • Loading branch information
bao-sotatek committed May 9, 2024
1 parent 3b0a775 commit a1cbf03
Show file tree
Hide file tree
Showing 20 changed files with 572 additions and 53 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"react-qrcode-logo": "^2.9.0",
"react-redux": "^8.0.5",
"react-router-dom": "^5.3.4",
"signify-ts": "github:WebOfTrust/signify-ts#faeebad4bdb29832cb25b83e41db064ede07dd36",
"signify-ts": "github:cardano-foundation/signify-ts#c1422b041d2ff53b3d4456a29443722b95f35fb5",
"swiper": "^9.2.0",
"web-vitals": "^2.1.4"
},
Expand Down
8 changes: 4 additions & 4 deletions services/credential-issuance-server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion services/credential-issuance-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"net": "^1.0.2",
"node-cache": "^5.1.2",
"qrcode-terminal": "^0.12.0",
"signify-ts": "github:WebOfTrust/signify-ts#faeebad4bdb29832cb25b83e41db064ede07dd36",
"signify-ts": "github:cardano-foundation/signify-ts#c1422b041d2ff53b3d4456a29443722b95f35fb5",
"uuid": "^9.0.1",
"ws": "^8.13.0"
},
Expand Down
40 changes: 40 additions & 0 deletions services/credential-issuance-server/src/agent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SignifyApi } from "./modules/signify/signifyApi";
import { NotificationRoute } from "./modules/signify/signifyApi.type";

class Agent {
static readonly ISSUER_AID_NAME = "issuer";
Expand Down Expand Up @@ -58,7 +59,46 @@ class Agent {
async contacts() {
return this.signifyApi.contacts();
}
async onNotificationKeriStateChanged() {
// eslint-disable-next-line no-constant-condition
while (true) {
const notifications = await this.signifyApi.getNotifications();
for (const notif of notifications.notes) {
await this.processNotification(notif);
}
await new Promise((rs) => {
setTimeout(() => {
rs(true);
}, 2000);
});
}
}

private async processNotification(notif: any) {
if (
Object.values(NotificationRoute).includes(
notif.a.r as NotificationRoute
) &&
!notif.r
) {
switch (notif.a.r) {
case NotificationRoute.ExnIpexOffer: {
const msg = await this.signifyApi.getExchangeMsg(notif.a.d!);
await this.signifyApi.agreeToAcdcFromOffer(
Agent.ISSUER_AID_NAME,
msg.exn.d,
msg.exn.i
);
break;
}
default:
break;
}
}
await this.signifyApi.deleteNotification(notif.i);
}
async initKeri(issuerName?: string) {
this.onNotificationKeriStateChanged();
const AIDIssuerName = issuerName ? issuerName : Agent.ISSUER_AID_NAME;
const existedIndentifier = await this.signifyApi
.getIdentifierByName(AIDIssuerName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class SignifyApi {
static readonly DEFAULT_ROLE = "agent";
static readonly FAILED_TO_RESOLVE_OOBI =
"Failed to resolve OOBI, operation not completing...";
static readonly UNKNOW_SCHEMA_ID = "Unknow Schema ID: "
static readonly UNKNOW_SCHEMA_ID = "Unknow Schema ID: ";
private signifyClient!: SignifyClient;
private opTimeout: number;
private opRetryInterval: number;
Expand Down Expand Up @@ -104,8 +104,8 @@ export class SignifyApi {
name?: string
) {
await this.resolveOobi(`${config.endpoint}/oobi/${schemaId}`);
let vcdata = {}

let vcdata = {};
if (schemaId === "EBIFDhtSE0cM4nbTnaMqiV1vUIlcnbsqBMeVMmeGmXOu") {
vcdata = {
attendeeName: name,
Expand All @@ -117,12 +117,17 @@ export class SignifyApi {
} else {
throw new Error(SignifyApi.UNKNOW_SCHEMA_ID + schemaId);
}

const result = await this.signifyClient
.credentials()
.issue({ issuerName, registryId, schemaId, recipient, data: vcdata });
await waitAndGetDoneOp(this.signifyClient, result.op, this.opTimeout, this.opRetryInterval);

await waitAndGetDoneOp(
this.signifyClient,
result.op,
this.opTimeout,
this.opRetryInterval
);

const datetime = new Date().toISOString().replace("Z", "000+00:00");
const [grant, gsigs, gend] = await this.signifyClient.ipex().grant({
senderName: issuerName,
Expand All @@ -137,21 +142,52 @@ export class SignifyApi {
.submitGrant(issuerName, grant, gsigs, gend, [recipient]);
}

async requestDisclosure(senderName: string, schemaSaid: string, recipient: string) {
/*const [apply, sigs] = await this.signifyClient.ipex().apply({
async requestDisclosure(
senderName: string,
schemaSaid: string,
recipient: string
) {
const [apply, sigs] = await this.signifyClient.ipex().apply({
senderName,
recipient,
schema: schemaSaid,
});
await this.signifyClient
.ipex()
.submitApply(senderName, apply, sigs, [recipient]);*/
.submitApply(senderName, apply, sigs, [recipient]);
}

async contacts(): Promise<any> {
return this.signifyClient.contacts().list();
}

async agreeToAcdcFromOffer(
senderName: string,
offerSaid: string,
recipient: string
) {
const [apply, sigs] = await this.signifyClient.ipex().agree({
senderName,
recipient,
offer: offerSaid,
});
await this.signifyClient
.ipex()
.submitAgree(senderName, apply, sigs, [recipient]);
}

async getNotifications() {
return this.signifyClient.notifications().list();
}

async deleteNotification(said: string) {
return this.signifyClient.notifications().delete(said);
}

async getExchangeMsg(said: string) {
return this.signifyClient.exchanges().get(said);
}

/**
* Note - op must be of type any here until Signify cleans up its typing.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
enum NotificationRoute {
ExnIpexOffer = "/exn/ipex/offer",
}
export { NotificationRoute };
2 changes: 1 addition & 1 deletion services/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
container_name: idw-keria
restart: unless-stopped
build:
context: github.com/WebOfTrust/keria#f24cf4b01932916cc640ff6f20ffda6f641c1ad2
context: github.com/WebOfTrust/keria#da8e53fe92b9027ec5b547c00d4f54f278fbb1b2
dockerfile: ./images/keria.dockerfile
environment:
- KERI_AGENT_CORS=true
Expand Down
4 changes: 3 additions & 1 deletion src/core/agent/agent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,11 @@ interface IdentifierResult {
}

enum NotificationRoute {
Credential = "/exn/ipex/grant",
ExnIpexGrant = "/exn/ipex/grant",
MultiSigIcp = "/multisig/icp",
MultiSigRot = "/multisig/rot",
ExnIpexApply = "/exn/ipex/apply",
ExnIpexAgree = "/exn/ipex/agree",
}

export {
Expand Down
1 change: 1 addition & 0 deletions src/core/agent/records/credentialMetadataRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CredentialMetadataRecord extends BaseRecord {
isArchived: this.isArchived,
isDeleted: this.isDeleted,
connectionId: this.connectionId,
id: this.id,
};
}
}
Expand Down
20 changes: 19 additions & 1 deletion src/core/agent/records/credentialStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const credentialMetadataRecordB = new CredentialMetadataRecord({
id: id2,
});

describe("Connection service of agent", () => {
describe("Credential storage test", () => {
beforeEach(() => {
jest.resetAllMocks();
});
Expand Down Expand Up @@ -88,4 +88,22 @@ describe("Connection service of agent", () => {
const record = await credentialStorage.getCredentialMetadata("id");
expect(record).toBe(null);
});

test("Should get credential by ids", async () => {
const ids = [credentialMetadataRecordA.id, credentialMetadataRecordB.id];
storageService.findAllByQuery.mockResolvedValue([
credentialMetadataRecordA,
credentialMetadataRecordB,
]);
expect(await credentialStorage.getCredentialMetadatasById(ids)).toEqual([
credentialMetadataRecordA,
credentialMetadataRecordB,
]);
expect(storageService.findAllByQuery).toBeCalledWith(
{
$or: ids.map((id) => ({ id })),
},
CredentialMetadataRecord
);
});
});
9 changes: 9 additions & 0 deletions src/core/agent/records/credentialStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ class CredentialStorage {
await this.storageService.update(record);
}
}

async getCredentialMetadatasById(ids: string[]) {
return this.storageService.findAllByQuery(
{
$or: ids.map((id) => ({ id })),
},
CredentialMetadataRecord
);
}
}

export { CredentialStorage };
2 changes: 1 addition & 1 deletion src/core/agent/records/identifierStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const identifierMetadataRecord2 = new IdentifierMetadataRecord({
id: "id2",
});

describe("Connection service of agent", () => {
describe("Identifier storage test", () => {
beforeEach(() => {
jest.resetAllMocks();
});
Expand Down
8 changes: 4 additions & 4 deletions src/core/agent/services/credentialService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,16 +405,16 @@ describe("Credential service of agent", () => {
).rejects.toThrowError(CredentialService.CREDENTIAL_NOT_FOUND);
});

test("Should be able to getUnhandledIpexGrantNotifications", async () => {
test("Should be able to getExnIpexGrantpexGrantNotifications", async () => {
const notificationRecord = {
_tags: {
isDismiss: true,
route: NotificationRoute.Credential,
route: NotificationRoute.ExnIpexGrant,
},
id: "AIeGgKkS23FDK4mxpfodpbWhTydFz2tdM64DER6EdgG-",
createdAt: new Date(),
a: {
r: NotificationRoute.Credential,
r: NotificationRoute.ExnIpexGrant,
d: "EF6Nmxz8hs0oVc4loyh2J5Sq9H3Z7apQVqjO6e4chtsp",
},
};
Expand All @@ -438,7 +438,7 @@ describe("Credential service of agent", () => {
isDismissed: false,
});
expect(notificationStorage.findAllByQuery).toBeCalledWith({
route: NotificationRoute.Credential,
route: NotificationRoute.ExnIpexGrant,
isDismissed: false,
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/core/agent/services/credentialService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class CredentialService extends AgentService {
} = {}
): Promise<KeriaNotification[]> {
const results = await this.notificationStorage.findAllByQuery({
route: NotificationRoute.Credential,
route: NotificationRoute.ExnIpexGrant,
...filters,
});
return results.map((result) => {
Expand Down

0 comments on commit a1cbf03

Please sign in to comment.