diff --git a/apps/shared/src/events/connectionEvents.ts b/apps/shared/src/events/connectionEvents.ts index 915f1e8a99b06f164a08642911997c422837deed..d8a6e5325a9e9ed8fa9ef8a2db838e4789174f5c 100644 --- a/apps/shared/src/events/connectionEvents.ts +++ b/apps/shared/src/events/connectionEvents.ts @@ -73,13 +73,11 @@ export type EventDidcommConnectionsReceiveInvitationFromUrlInput = BaseEventInput<{ invitationUrl: string; }>; -export class EventDidcommConnectionsReceiveInvitationFromUrl extends BaseEvent<ConnectionRecord | null> { +export class EventDidcommConnectionsReceiveInvitationFromUrl extends BaseEvent<ConnectionRecord> { public static token = 'didcomm.connections.receiveInvitationFromUrl'; public get instance() { - return this.data - ? JsonTransformer.fromJSON(this.data, ConnectionRecord) - : null; + return JsonTransformer.fromJSON(this.data, ConnectionRecord); } public static fromEvent(e: EventDidcommConnectionsReceiveInvitationFromUrl) { diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts index b539927fb7f6c7fdeedb56853fbddf3c614d9087..b84fed5e85c0d30928e180e85671ed21fd1022d1 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -78,7 +78,6 @@ export class AgentService implements OnApplicationShutdown { }); this.agent.registerInboundTransport(httpInbound); - this.agent.registerOutboundTransport(new WsOutboundTransport()); this.agent.registerOutboundTransport(new HttpOutboundTransport()); } @@ -96,7 +95,7 @@ export class AgentService implements OnApplicationShutdown { key: walletKey, }, endpoints, - logger: new AgentLogger(LogLevel.warn), + logger: new AgentLogger(LogLevel.off), }; } diff --git a/apps/ssi-abstraction/test/connections.e2e-spec.ts b/apps/ssi-abstraction/test/connections.e2e-spec.ts index 7579cac7f39bb7b7100ac201e5d48155a0d96917..e34832b8ca93c3a0098cb0e38bc5d35a637e9784 100644 --- a/apps/ssi-abstraction/test/connections.e2e-spec.ts +++ b/apps/ssi-abstraction/test/connections.e2e-spec.ts @@ -9,7 +9,7 @@ import type { EventDidcommConnectionsCreateInvitationInput, } from '@ocm/shared'; -import { ConnectionRecord } from '@credo-ts/core'; +import { ConnectionRecord, DidExchangeState } from '@credo-ts/core'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { @@ -34,6 +34,7 @@ describe('Connections', () => { let app: INestApplication; let client: ClientProxy; let tenantId: string; + let tenantIdTwo: string; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -59,6 +60,9 @@ describe('Connections', () => { const ts = app.get(TenantsService); const { id } = await ts.create({ label: TOKEN }); tenantId = id; + + const { id: idTwo } = await ts.create({ label: `${TOKEN}-two` }); + tenantIdTwo = idTwo; }); afterAll(async () => { @@ -138,6 +142,53 @@ describe('Connections', () => { expect(eventInstance.instance).toBeInstanceOf(ConnectionRecord); }); + it(`Create and receive invitation between tenants`, async () => { + const createInvitationResponse$ = client.send< + EventDidcommConnectionsCreateInvitation, + EventDidcommConnectionsCreateInvitationInput + >(EventDidcommConnectionsCreateInvitation.token, { + tenantId: tenantIdTwo, + }); + const createInvitationResponse = await firstValueFrom( + createInvitationResponse$, + ); + const { + instance: { invitationUrl }, + } = EventDidcommConnectionsCreateInvitation.fromEvent( + createInvitationResponse, + ); + + const response$ = client.send< + EventDidcommConnectionsReceiveInvitationFromUrl, + EventDidcommConnectionsReceiveInvitationFromUrlInput + >(EventDidcommConnectionsReceiveInvitationFromUrl.token, { + invitationUrl, + tenantId, + }); + const response = await firstValueFrom(response$); + const { instance: connectionRecord } = + EventDidcommConnectionsReceiveInvitationFromUrl.fromEvent(response); + + await new Promise((r) => setTimeout(r, 2000)); + + const getByIdResponse$ = client.send< + EventDidcommConnectionsGetById, + EventDidcommConnectionsGetByIdInput + >(EventDidcommConnectionsGetById.token, { + tenantId, + id: connectionRecord.id, + }); + const getByIdResponse = await firstValueFrom(getByIdResponse$); + const { instance: maybeRecord } = + EventDidcommConnectionsGetById.fromEvent(getByIdResponse); + + if (!maybeRecord) { + throw new Error(`Record not found with id: ${connectionRecord.id}`); + } + + expect(maybeRecord.state).toStrictEqual(DidExchangeState.Completed); + }); + it(EventDidcommConnectionsCreateWithSelf.token, async () => { const response$ = client.send< EventDidcommConnectionsCreateWithSelf, diff --git a/apps/ssi-abstraction/test/revocation.e2e-spec.ts b/apps/ssi-abstraction/test/revocation.e2e-spec.ts index 57af89da8c0c26f7c23d5bcde3fff7f177e99fe2..a2ae0870b66e042c91d055e906acce7d11a7c551 100644 --- a/apps/ssi-abstraction/test/revocation.e2e-spec.ts +++ b/apps/ssi-abstraction/test/revocation.e2e-spec.ts @@ -5,14 +5,21 @@ import type { EventAnonCredsRevocationRegisterRevocationRegistryDefinitionInput, EventAnonCredsRevocationRegisterRevocationStatusListInput, EventAnonCredsRevocationRevokeInput, - EventDidcommAnonCredsCredentialsOfferToSelfInput, + EventDidcommAnonCredsCredentialsOfferInput, + EventDidcommConnectionsCreateInvitationInput, + EventDidcommConnectionsGetByIdInput, + EventDidcommConnectionsReceiveInvitationFromUrlInput, } from '@ocm/shared'; +import { DidExchangeState } from '@credo-ts/core'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { + EventDidcommConnectionsGetById, + EventDidcommAnonCredsCredentialsOffer, + EventDidcommConnectionsReceiveInvitationFromUrl, + EventDidcommConnectionsCreateInvitation, EventAnonCredsRevocationCheckCredentialStatus, - EventDidcommAnonCredsCredentialsOfferToSelf, EventAnonCredsRevocationRegisterRevocationRegistryDefinition, EventAnonCredsRevocationRegisterRevocationStatusList, EventAnonCredsRevocationRevoke, @@ -24,7 +31,6 @@ import { firstValueFrom } from 'rxjs'; import { AgentModule } from '../src/agent/agent.module.js'; import { AnonCredsCredentialsModule } from '../src/agent/anoncredsCredentials/anoncredsCredentials.module.js'; import { ConnectionsModule } from '../src/agent/connections/connections.module.js'; -import { ConnectionsService } from '../src/agent/connections/connections.service.js'; import { CredentialDefinitionsModule } from '../src/agent/credentialDefinitions/credentialDefinitions.module.js'; import { CredentialDefinitionsService } from '../src/agent/credentialDefinitions/credentialDefinitions.service.js'; import { DidsModule } from '../src/agent/dids/dids.module.js'; @@ -40,10 +46,13 @@ describe('Revocation', () => { const TOKEN = 'REVOCATION_CLIENT_SERVICE'; let app: INestApplication; let client: ClientProxy; + let tenantId: string; + let tenantIdTwo: string; let issuerDid: string; let credentialDefinitionId: string; + let connectionId: string; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -75,8 +84,10 @@ describe('Revocation', () => { const { id } = await tenantsService.create({ label: TOKEN }); tenantId = id; - const connectionsService = app.get(ConnectionsService); - await connectionsService.createConnectionWithSelf({ tenantId }); + const { id: idTwo } = await tenantsService.create({ + label: `${TOKEN}-two`, + }); + tenantIdTwo = idTwo; const ds = app.get(DidsService); await ds.registerEndorserDids(); @@ -113,7 +124,7 @@ describe('Revocation', () => { client.close(); }); - xit( + it( EventAnonCredsRevocationRegisterRevocationRegistryDefinition.token, async () => { const response$ = client.send< @@ -221,13 +232,65 @@ describe('Revocation', () => { expect(eventInstance.instance).toMatchObject({}); } + // Establish a connection with another tenant + { + const createInvitationResponse$ = client.send< + EventDidcommConnectionsCreateInvitation, + EventDidcommConnectionsCreateInvitationInput + >(EventDidcommConnectionsCreateInvitation.token, { + tenantId: tenantIdTwo, + }); + const createInvitationResponse = await firstValueFrom( + createInvitationResponse$, + ); + const { + instance: { invitationUrl }, + } = EventDidcommConnectionsCreateInvitation.fromEvent( + createInvitationResponse, + ); + + const response$ = client.send< + EventDidcommConnectionsReceiveInvitationFromUrl, + EventDidcommConnectionsReceiveInvitationFromUrlInput + >(EventDidcommConnectionsReceiveInvitationFromUrl.token, { + invitationUrl, + tenantId, + }); + const response = await firstValueFrom(response$); + const { instance: connectionRecord } = + EventDidcommConnectionsReceiveInvitationFromUrl.fromEvent(response); + + // Wait for the connection to be established + await new Promise((r) => setTimeout(r, 2000)); + + const getByIdResponse$ = client.send< + EventDidcommConnectionsGetById, + EventDidcommConnectionsGetByIdInput + >(EventDidcommConnectionsGetById.token, { + tenantId, + id: connectionRecord.id, + }); + const getByIdResponse = await firstValueFrom(getByIdResponse$); + const { instance: maybeRecord } = + EventDidcommConnectionsGetById.fromEvent(getByIdResponse); + + if (!maybeRecord) { + throw new Error(`Record not found with id: ${connectionRecord.id}`); + } + + expect(maybeRecord.state).toStrictEqual(DidExchangeState.Completed); + + connectionId = connectionRecord.id; + } + // Issue a credential { const response$ = client.send< - EventDidcommAnonCredsCredentialsOfferToSelf, - EventDidcommAnonCredsCredentialsOfferToSelfInput - >(EventDidcommAnonCredsCredentialsOfferToSelf.token, { + EventDidcommAnonCredsCredentialsOffer, + EventDidcommAnonCredsCredentialsOfferInput + >(EventDidcommAnonCredsCredentialsOffer.token, { tenantId, + connectionId, attributes: [ { name: 'Name', value: 'Berend' }, { name: 'Age', value: '30' }, @@ -237,7 +300,7 @@ describe('Revocation', () => { }); const response = await firstValueFrom(response$); const eventInstance = - EventDidcommAnonCredsCredentialsOfferToSelf.fromEvent(response); + EventDidcommAnonCredsCredentialsOffer.fromEvent(response); credentialId = eventInstance.instance.id; }