diff --git a/apps/shared/package.json b/apps/shared/package.json index 8bd91aba98c275d2a099f75f9054d8647874cc54..e092ae552055278ec57d94efd8a8623dd2c7fccf 100644 --- a/apps/shared/package.json +++ b/apps/shared/package.json @@ -21,6 +21,7 @@ "test:e2e": "jest --config ./test/jest.config.js" }, "dependencies": { + "@aries-framework/anoncreds": "0.4.2", "@aries-framework/core": "0.4.2", "@aries-framework/tenants": "^0.4.2", "@elastic/ecs-winston-format": "^1.5.0", diff --git a/apps/shared/src/events/didEvents.ts b/apps/shared/src/events/didEvents.ts index 60f5c6854f6343c4e0c0832901044ed155c0a650..e300545be74213c4172a34a216ecdd7eaf50c21a 100644 --- a/apps/shared/src/events/didEvents.ts +++ b/apps/shared/src/events/didEvents.ts @@ -4,44 +4,36 @@ import { DidDocument, JsonTransformer } from '@aries-framework/core'; import { BaseEvent } from './baseEvents.js'; -/** - * - * @todo: this should be removed as it is a weird event that should not be needed - * - */ -export type EventDidsPublicDidInput = BaseEventInput; -/** - * - * @todo: this should be removed as it is a weird event that should not be needed - * - */ -export class EventDidsPublicDid extends BaseEvent<DidDocument> { - public static token = 'dids.publicDid'; +export type EventDidsResolveInput = BaseEventInput<{ did: string }>; +export class EventDidsResolve extends BaseEvent<DidDocument> { + public static token = 'dids.resolve'; public get instance() { return JsonTransformer.fromJSON(this.data, DidDocument); } - public static fromEvent(e: EventDidsPublicDid) { - return new EventDidsPublicDid( - e.data, - e.tenantId, - e.id, - e.type, - e.timestamp, - ); + public static fromEvent(e: EventDidsResolve) { + return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp); } } -export type EventDidsResolveInput = BaseEventInput<{ did: string }>; -export class EventDidsResolve extends BaseEvent<DidDocument> { - public static token = 'dids.resolve'; +export type EventDidsRegisterIndyFromSeedInput = BaseEventInput<{ + seed: string; +}>; +export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> { + public static token = 'dids.register.indy.fromSeed'; public get instance() { - return JsonTransformer.fromJSON(this.data, DidDocument); + return this.data; } - public static fromEvent(e: EventDidsResolve) { - return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp); + public static fromEvent(e: EventDidsRegisterIndyFromSeed) { + return new EventDidsRegisterIndyFromSeed( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); } } diff --git a/apps/shared/src/events/schemaEvents.ts b/apps/shared/src/events/schemaEvents.ts new file mode 100644 index 0000000000000000000000000000000000000000..29cb53b233bd9191134ecf5606a676d784714b78 --- /dev/null +++ b/apps/shared/src/events/schemaEvents.ts @@ -0,0 +1,70 @@ +import type { BaseEventInput } from './baseEvents.js'; +import type { AnonCredsSchema } from '@aries-framework/anoncreds'; + +import { BaseEvent } from './baseEvents.js'; + +export type EventAnonCredsSchemasGetAllInput = BaseEventInput; +export class EventAnonCredsSchemasGetAll extends BaseEvent< + Array<AnonCredsSchema> +> { + public static token = 'anoncreds.schemas.getAll'; + + public get instance() { + return this.data; + } + + public static fromEvent(e: EventAnonCredsSchemasGetAll) { + return new EventAnonCredsSchemasGetAll( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); + } +} + +export type EventAnonCredsSchemasGetByIdInput = BaseEventInput<{ + schemaId: string; +}>; +export class EventAnonCredsSchemasGetById extends BaseEvent<AnonCredsSchema | null> { + public static token = 'anoncreds.schemas.getById'; + + public get instance() { + return this.data; + } + + public static fromEvent(e: EventAnonCredsSchemasGetById) { + return new EventAnonCredsSchemasGetById( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); + } +} + +export type EventAnonCredsSchemasRegisterInput = BaseEventInput<{ + issuerDid: string; + name: string; + version: string; + attributeNames: Array<string>; +}>; +export class EventAnonCredsSchemasRegister extends BaseEvent<AnonCredsSchema> { + public static token = 'anoncreds.schemas.register'; + + public get instance() { + return this.data; + } + + public static fromEvent(e: EventAnonCredsSchemasRegister) { + return new EventAnonCredsSchemasRegister( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); + } +} diff --git a/apps/shared/src/index.ts b/apps/shared/src/index.ts index 42fe80034abf82559f144a6549e28bfc314b6b55..7f8ae5efe78d58642c60d2b62c1cd718d0e501e5 100644 --- a/apps/shared/src/index.ts +++ b/apps/shared/src/index.ts @@ -7,3 +7,4 @@ export * from './logging/logAxiosError.js'; export * from './events/connectionEvents.js'; export * from './events/didEvents.js'; export * from './events/tenantEvents.js'; +export * from './events/schemaEvents.js'; diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts index 76b0bad51df5ce8abd267e2f08d56bf83f86de1c..9df1a7034eb1dec7eb9352566ea01366f26e7786 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -16,11 +16,9 @@ import { JwkDidResolver, KeyDidRegistrar, KeyDidResolver, - KeyType, LogLevel, PeerDidRegistrar, PeerDidResolver, - TypedArrayEncoder, WebDidResolver, } from '@aries-framework/core'; import { @@ -40,7 +38,6 @@ import { logger } from '@ocm/shared'; import { LEDGERS } from '../config/ledger.js'; -import { registerPublicDids } from './ledger/register.js'; import { AgentLogger } from './logger.js'; export type AppAgent = Agent<AgentService['modules']>; @@ -127,7 +124,7 @@ export class AgentService implements OnApplicationShutdown { }; } - public get ledgers() { + private get ledgers() { const ledgerIds = this.configService.get('agent.ledgerIds'); if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') { @@ -153,41 +150,8 @@ export class AgentService implements OnApplicationShutdown { }); } - private async registerPublicDid() { - const { publicDidSeed, ledgerIds } = this.configService.get('agent'); - - if (!publicDidSeed) { - logger.info('No public did seed provided, skipping registration'); - return; - } - - if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') { - return; - } - - const registeredPublicDidResponses = await registerPublicDids({ - alias: this.config.label, - ledgerIds, - seed: publicDidSeed, - }); - - for (const publicDidResponse of registeredPublicDidResponses) { - await this.agent.dids.import({ - overwrite: true, - did: publicDidResponse.did, - privateKeys: [ - { - keyType: KeyType.Ed25519, - privateKey: TypedArrayEncoder.fromString(publicDidSeed), - }, - ], - }); - } - } - public async onModuleInit() { await this.agent.initialize(); - await this.registerPublicDid(); logger.info('Agent initialized'); } diff --git a/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts b/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts index eb513d9d30947af2b8a02112ec885911fa424805..7bdb15e4d35aadc60eca85b1b1c056b094cf41d8 100644 --- a/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts +++ b/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts @@ -34,4 +34,20 @@ describe('DidsController', () => { expect(event.data).toStrictEqual(result); }); }); + + describe('register indy did from seed', () => { + it('should register an indy did from seed', async () => { + const result = ['did:indy:bcovrin:test:mock']; + jest + .spyOn(didsService, 'registerDidIndyFromSeed') + .mockResolvedValue(result); + + const event = await didsController.registerFromSeed({ + seed: 'random-secure-seed', + tenantId: 'some-id', + }); + + expect(event.data).toStrictEqual(result); + }); + }); }); diff --git a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts index c869d0db7f116abed9397c663753c5d2a52a3ad0..688d68862c41553b2e9d83fc60d16aad11a741a1 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts @@ -1,8 +1,8 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { - EventDidsPublicDid, - EventDidsPublicDidInput, + EventDidsRegisterIndyFromSeed, + EventDidsRegisterIndyFromSeedInput, EventDidsResolve, EventDidsResolveInput, } from '@ocm/shared'; @@ -13,10 +13,10 @@ import { DidsService } from './dids.service.js'; export class DidsController { public constructor(private didsService: DidsService) {} - @MessagePattern(EventDidsPublicDid.token) - public async publicDid(options: EventDidsPublicDidInput) { - return new EventDidsPublicDid( - await this.didsService.getPublicDid(options), + @MessagePattern(EventDidsRegisterIndyFromSeed.token) + public async registerFromSeed(options: EventDidsRegisterIndyFromSeedInput) { + return new EventDidsRegisterIndyFromSeed( + await this.didsService.registerDidIndyFromSeed(options), options.tenantId, ); } diff --git a/apps/ssi-abstraction/src/agent/dids/dids.module.ts b/apps/ssi-abstraction/src/agent/dids/dids.module.ts index 18668fe548266d2334051dc2f806eecb824060f7..d0a3f1559bf3705567da090720d4164fb3910a86 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.module.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; import { AgentModule } from '../agent.module.js'; @@ -6,7 +7,7 @@ import { DidsController } from './dids.controller.js'; import { DidsService } from './dids.service.js'; @Module({ - imports: [AgentModule ], + imports: [AgentModule, ConfigModule], providers: [DidsService], controllers: [DidsController], }) diff --git a/apps/ssi-abstraction/src/agent/dids/dids.service.ts b/apps/ssi-abstraction/src/agent/dids/dids.service.ts index 2adf4c401c4c9d93e10e5f79ff1185bbd6cdf5a8..cf7807236f57ca373f86fe84a05b95dba260dda5 100644 --- a/apps/ssi-abstraction/src/agent/dids/dids.service.ts +++ b/apps/ssi-abstraction/src/agent/dids/dids.service.ts @@ -1,18 +1,23 @@ -import type { - EventDidsPublicDidInput, - EventDidsResolveInput, -} from '@ocm/shared'; +import type { EventDidsResolveInput } from '@ocm/shared'; +import { KeyType, TypedArrayEncoder } from '@aries-framework/core'; import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { registerPublicDids } from '../ledger/register.js'; import { WithTenantService } from '../withTenantService.js'; @Injectable() export class DidsService { - public withTenantService: WithTenantService; + private withTenantService: WithTenantService; + private configService: ConfigService; - public constructor(withTenantService: WithTenantService) { + public constructor( + withTenantService: WithTenantService, + configService: ConfigService, + ) { this.withTenantService = withTenantService; + this.configService = configService; } public async resolve({ did, tenantId }: EventDidsResolveInput) { @@ -34,26 +39,35 @@ export class DidsService { }); } - public async getPublicDid({ tenantId }: EventDidsPublicDidInput) { - return this.withTenantService.invoke(tenantId, async (t) => { - const dids = await t.dids.getCreatedDids({ method: 'indy' }); - if (dids.length === 0) { - throw new Error('No registered public DIDs'); - } + public async registerDidIndyFromSeed({ + tenantId, + seed, + }: { + tenantId: string; + seed: string; + }): Promise<Array<string>> { + const ledgerIds = this.configService.get('agent.ledgerIds'); - if (dids.length > 1) { - throw new Error('Multiple public DIDs found'); - } + const registeredPublicDidResponses = await registerPublicDids({ + ledgerIds, + seed, + }); - const didRecord = dids[0]; + for (const publicDidResponse of registeredPublicDidResponses) { + await this.withTenantService.invoke(tenantId, (t) => + t.dids.import({ + overwrite: true, + did: publicDidResponse.did, + privateKeys: [ + { + keyType: KeyType.Ed25519, + privateKey: TypedArrayEncoder.fromString(seed), + }, + ], + }), + ); + } - if (!didRecord.didDocument) { - throw new Error( - 'A public DID was found, but did not include a DID Document', - ); - } - - return didRecord.didDocument; - }); + return registeredPublicDidResponses.map((r) => r.did); } } diff --git a/apps/ssi-abstraction/src/agent/ledger/register.ts b/apps/ssi-abstraction/src/agent/ledger/register.ts index 1396428d29e68d21ab7df21390bdd070fdb1336c..8600a454b48e609e2798aba6482cbd719720c419 100644 --- a/apps/ssi-abstraction/src/agent/ledger/register.ts +++ b/apps/ssi-abstraction/src/agent/ledger/register.ts @@ -6,14 +6,12 @@ import axios from 'axios'; import { LEDGERS } from '../../config/ledger.js'; type RegisterPublicDidOptions = { - alias: string; ledgerIds: Array<LedgerIds>; seed: string; }; type LedgerRegistrationBody = { role?: 'ENDORSER'; - alias?: string; seed: string; }; @@ -25,7 +23,6 @@ type RegisterPublicDidResponse = { export const registerPublicDids = async ({ ledgerIds, - alias, seed, }: RegisterPublicDidOptions): Promise<Array<RegisterPublicDidResponse>> => { const responses: Array<RegisterPublicDidResponse> = []; @@ -36,7 +33,6 @@ export const registerPublicDids = async ({ const body: LedgerRegistrationBody = { role: 'ENDORSER', - alias, seed, }; diff --git a/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts b/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d761a213358d7eb77c68c21053480efa68193867 --- /dev/null +++ b/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts @@ -0,0 +1,74 @@ +import type { AnonCredsSchema } from '@aries-framework/anoncreds'; + +import { Test } from '@nestjs/testing'; + +import { mockConfigModule } from '../../../config/__tests__/mockConfig.js'; +import { AgentModule } from '../../agent.module.js'; +import { SchemasController } from '../schemas.controller.js'; +import { SchemasService } from '../schemas.service.js'; + +describe('ConnectionsController', () => { + let schemasController: SchemasController; + let schemasService: SchemasService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [mockConfigModule(), AgentModule], + controllers: [SchemasController], + providers: [SchemasService], + }).compile(); + + schemasService = moduleRef.get(SchemasService); + schemasController = moduleRef.get(SchemasController); + }); + + describe('get all', () => { + it('should get all the registered schemas of the agent', async () => { + const result: Array<AnonCredsSchema> = []; + jest.spyOn(schemasService, 'getAll').mockResolvedValue(result); + + const event = await schemasController.getAll({ + tenantId: 'some-id', + }); + + expect(event.data).toStrictEqual(result); + }); + }); + + describe('get by id', () => { + it('should get a schema by id', async () => { + const result: AnonCredsSchema | null = null; + jest.spyOn(schemasService, 'getById').mockResolvedValue(result); + + const event = await schemasController.getById({ + schemaId: 'id', + tenantId: 'some-id', + }); + + expect(event.data).toStrictEqual(result); + }); + }); + + describe('register schema', () => { + it('should register a schema on a ledger', async () => { + const result: AnonCredsSchema = { + name: 'schema-name', + version: '1.0', + issuerId: 'did:indy:123', + attrNames: ['name', 'age'], + }; + + jest.spyOn(schemasService, 'register').mockResolvedValue(result); + + const event = await schemasController.register({ + tenantId: 'some-id', + version: '1.0', + name: 'schema-name', + issuerDid: 'did:indy:123', + attributeNames: ['name', 'age'], + }); + + expect(event.data).toStrictEqual(result); + }); + }); +}); diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..180fd98b3be90fd778cef061ae2c415acbf54f7e --- /dev/null +++ b/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts @@ -0,0 +1,47 @@ +import { Controller } from '@nestjs/common'; +import { MessagePattern } from '@nestjs/microservices'; +import { + EventAnonCredsSchemasGetAll, + EventAnonCredsSchemasGetAllInput, + EventAnonCredsSchemasGetById, + EventAnonCredsSchemasGetByIdInput, + EventAnonCredsSchemasRegister, + EventAnonCredsSchemasRegisterInput, +} from '@ocm/shared'; + +import { SchemasService } from './schemas.service.js'; + +@Controller('schemas') +export class SchemasController { + public constructor(private schemasService: SchemasService) {} + + @MessagePattern(EventAnonCredsSchemasGetAll.token) + public async getAll( + options: EventAnonCredsSchemasGetAllInput, + ): Promise<EventAnonCredsSchemasGetAll> { + return new EventAnonCredsSchemasGetAll( + await this.schemasService.getAll(options), + options.tenantId, + ); + } + + @MessagePattern(EventAnonCredsSchemasGetById.token) + public async getById( + options: EventAnonCredsSchemasGetByIdInput, + ): Promise<EventAnonCredsSchemasGetById> { + return new EventAnonCredsSchemasGetById( + await this.schemasService.getById(options), + options.tenantId, + ); + } + + @MessagePattern(EventAnonCredsSchemasRegister.token) + public async register( + options: EventAnonCredsSchemasRegisterInput, + ): Promise<EventAnonCredsSchemasRegister> { + return new EventAnonCredsSchemasRegister( + await this.schemasService.register(options), + options.tenantId, + ); + } +} diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3faaad8d8902a6551cd0e3cab57313e5e925076 --- /dev/null +++ b/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; + +import { AgentModule } from '../agent.module.js'; + +import { SchemasController } from './schemas.controller.js'; +import { SchemasService } from './schemas.service.js'; + +@Module({ + imports: [AgentModule], + providers: [SchemasService], + controllers: [SchemasController], +}) +export class SchemasModule {} diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..da3a7dc0c6f8b1e7a869d902fb0d3b8abcb10adf --- /dev/null +++ b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts @@ -0,0 +1,72 @@ +import type { AnonCredsSchema } from '@aries-framework/anoncreds'; +import type { IndyVdrRegisterSchemaOptions } from '@aries-framework/indy-vdr'; +import type { + EventAnonCredsSchemasGetAllInput, + EventAnonCredsSchemasGetByIdInput, + EventAnonCredsSchemasRegisterInput, +} from '@ocm/shared'; + +import { Injectable } from '@nestjs/common'; + +import { WithTenantService } from '../withTenantService.js'; + +@Injectable() +export class SchemasService { + public withTenantService: WithTenantService; + + public constructor(withTenantService: WithTenantService) { + this.withTenantService = withTenantService; + } + + public async getAll({ + tenantId, + }: EventAnonCredsSchemasGetAllInput): Promise<Array<AnonCredsSchema>> { + return this.withTenantService.invoke(tenantId, async (t) => + (await t.modules.anoncreds.getCreatedSchemas({})).map((r) => r.schema), + ); + } + + public async getById({ + tenantId, + schemaId, + }: EventAnonCredsSchemasGetByIdInput): Promise<AnonCredsSchema | null> { + return this.withTenantService.invoke(tenantId, async (t) => { + const { schema } = await t.modules.anoncreds.getSchema(schemaId); + return schema ?? null; + }); + } + + public async register({ + tenantId, + name, + version, + issuerDid, + attributeNames, + }: EventAnonCredsSchemasRegisterInput): Promise<AnonCredsSchema> { + return this.withTenantService.invoke(tenantId, async (t) => { + const { schemaState } = + await t.modules.anoncreds.registerSchema<IndyVdrRegisterSchemaOptions>({ + schema: { + version, + name, + issuerId: issuerDid, + attrNames: attributeNames, + }, + options: { + endorserMode: 'external', + endorserDid: issuerDid, + }, + }); + + if (schemaState.state !== 'finished' && schemaState.state !== 'action') { + throw new Error( + `Error registering schema: ${ + schemaState.state === 'failed' ? schemaState.reason : 'Not Finished' + }`, + ); + } + + return schemaState.schema; + }); + } +} diff --git a/apps/ssi-abstraction/src/app.module.ts b/apps/ssi-abstraction/src/app.module.ts index 44a25ae0403ab4bd45ec9a8663f09231f491ff2c..52662f0ae58d187ef48317b71e3b77d7db74ef30 100644 --- a/apps/ssi-abstraction/src/app.module.ts +++ b/apps/ssi-abstraction/src/app.module.ts @@ -6,6 +6,7 @@ import { HealthController } from '@ocm/shared'; import { AgentModule } from './agent/agent.module.js'; import { ConnectionsModule } from './agent/connections/connections.module.js'; +import { SchemasModule } from './agent/schemas/schemas.module.js'; import { TenantsModule } from './agent/tenants/tenants.module.js'; import { config } from './config/config.js'; import { validationSchema } from './config/validation.js'; @@ -21,6 +22,7 @@ import { validationSchema } from './config/validation.js'; AgentModule, ConnectionsModule, DidsModule, + SchemasModule, TenantsModule, ], controllers: [HealthController], diff --git a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts index 8a5eb2ed2efb4ddbcbea7f3d4975a60a29a83af7..202a2b05ede19e8d39aa47dc5790dcb89157c001 100644 --- a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts +++ b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts @@ -5,7 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { validationSchema } from '../validation.js'; -const mockConfig = (port: number = 3001): AppConfig => ({ +const mockConfig = (port: number = 3001, withLedger = false): AppConfig => ({ agentHost: '', port: 3000, jwtSecret: '', @@ -16,19 +16,19 @@ const mockConfig = (port: number = 3001): AppConfig => ({ name: 'my-test-agent', walletId: utils.uuid(), walletKey: 'some-key', - ledgerIds: [], + ledgerIds: withLedger ? ['BCOVRIN_TEST'] : [], host: 'http://localhost', inboundPort: port, path: '', - publicDidSeed: '', + publicDidSeed: withLedger ? '12312367897123300000000000000000' : '', autoAcceptConnection: true, autoAcceptCredential: AutoAcceptCredential.ContentApproved, }, }); -export const mockConfigModule = (port: number = 3000) => +export const mockConfigModule = (port: number = 3000, withLedger = false) => ConfigModule.forRoot({ - load: [() => mockConfig(port)], + load: [() => mockConfig(port, withLedger)], validationSchema, }); diff --git a/apps/ssi-abstraction/test/dids.e2e-spec.ts b/apps/ssi-abstraction/test/dids.e2e-spec.ts index 8f3bafe813ab0b21b5c7ce63ab71df7e7da923ca..52c8a7b66caaba8d59e7d2c64d98ce725e977532 100644 --- a/apps/ssi-abstraction/test/dids.e2e-spec.ts +++ b/apps/ssi-abstraction/test/dids.e2e-spec.ts @@ -1,37 +1,21 @@ import type { INestApplication } from '@nestjs/common'; import type { ClientProxy } from '@nestjs/microservices'; import type { + EventDidsRegisterIndyFromSeedInput, EventDidsResolveInput, - EventDidsPublicDidInput, } from '@ocm/shared'; -import { DidDocument } from '@aries-framework/core'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; -import { EventDidsResolve, EventDidsPublicDid } from '@ocm/shared'; +import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared'; import { firstValueFrom } from 'rxjs'; import { AgentModule } from '../src/agent/agent.module.js'; import { DidsModule } from '../src/agent/dids/dids.module.js'; -import { DidsService } from '../src/agent/dids/dids.service.js'; import { TenantsModule } from '../src/agent/tenants/tenants.module.js'; import { TenantsService } from '../src/agent/tenants/tenants.service.js'; import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; -const mockDidDocument = { - context: ['https://w3id.org/did/v1'], - id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM', - verificationMethod: [ - { - id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey', - type: 'Ed25519VerificationKey2018', - controller: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM', - publicKeyBase58: '4SySYXQUtuK26zfC7RpQpWYMThfbXphUf8LWyXXmxyTX', - }, - ], - authentication: ['did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey'], -}; - describe('Dids', () => { const TOKEN = 'DIDS_CLIENT_SERVICE'; let app: INestApplication; @@ -39,12 +23,9 @@ describe('Dids', () => { let tenantId: string; beforeAll(async () => { - jest - .spyOn(DidsService.prototype, 'getPublicDid') - .mockResolvedValue(new DidDocument(mockDidDocument)); const moduleRef = await Test.createTestingModule({ imports: [ - mockConfigModule(3005), + mockConfigModule(3005, true), AgentModule, DidsModule, TenantsModule, @@ -72,16 +53,21 @@ describe('Dids', () => { client.close(); }); - it(EventDidsPublicDid.token, async () => { - const response$ = client.send<EventDidsPublicDid, EventDidsPublicDidInput>( - EventDidsPublicDid.token, - { tenantId }, - ); + it(EventDidsRegisterIndyFromSeed.token, async () => { + const response$ = client.send< + EventDidsRegisterIndyFromSeed, + EventDidsRegisterIndyFromSeedInput + >(EventDidsRegisterIndyFromSeed.token, { + seed: '12312367897123300000000000000000', + tenantId, + }); const response = await firstValueFrom(response$); - const eventInstance = EventDidsPublicDid.fromEvent(response); + const eventInstance = EventDidsRegisterIndyFromSeed.fromEvent(response); - expect(eventInstance.instance).toMatchObject(mockDidDocument); + expect(eventInstance.instance).toMatchObject( + expect.arrayContaining(['did:indy:bcovrin:test:9MMeff63VnCpogD2FWfKnJ']), + ); }); it(EventDidsResolve.token, async () => { diff --git a/apps/ssi-abstraction/test/schemas.e2e-spec.ts b/apps/ssi-abstraction/test/schemas.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..acfe669b6abbf07bec1d6cc50546991d29e353c1 --- /dev/null +++ b/apps/ssi-abstraction/test/schemas.e2e-spec.ts @@ -0,0 +1,119 @@ +import type { INestApplication } from '@nestjs/common'; +import type { ClientProxy } from '@nestjs/microservices'; +import type { + EventAnonCredsSchemasGetAllInput, + EventAnonCredsSchemasGetByIdInput, + EventAnonCredsSchemasRegisterInput, +} from '@ocm/shared'; + +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; +import { + EventAnonCredsSchemasGetAll, + EventAnonCredsSchemasGetById, + EventAnonCredsSchemasRegister, +} from '@ocm/shared'; +import { firstValueFrom } from 'rxjs'; + +import { AgentModule } from '../src/agent/agent.module.js'; +import { DidsModule } from '../src/agent/dids/dids.module.js'; +import { DidsService } from '../src/agent/dids/dids.service.js'; +import { SchemasModule } from '../src/agent/schemas/schemas.module.js'; +import { TenantsModule } from '../src/agent/tenants/tenants.module.js'; +import { TenantsService } from '../src/agent/tenants/tenants.service.js'; +import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; + +describe('Schemas', () => { + const TOKEN = 'SCHEMAS_CLIENT_SERVICE'; + let app: INestApplication; + let client: ClientProxy; + let tenantId: string; + let issuerDid: string; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + mockConfigModule(3004, true), + AgentModule, + SchemasModule, + TenantsModule, + DidsModule, + ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]), + ], + }).compile(); + + app = moduleRef.createNestApplication(); + + app.connectMicroservice({ transport: Transport.NATS }); + + await app.startAllMicroservices(); + await app.init(); + + client = app.get(TOKEN); + await client.connect(); + + const ts = app.get(TenantsService); + const { id } = await ts.create(TOKEN); + tenantId = id; + + const ds = app.get(DidsService); + const [did] = await ds.registerDidIndyFromSeed({ + tenantId, + seed: '12312367897123300000000000000000', + }); + issuerDid = did; + }); + + afterAll(async () => { + await app.close(); + client.close(); + }); + + it(EventAnonCredsSchemasGetAll.token, async () => { + const response$ = client.send< + EventAnonCredsSchemasGetAll, + EventAnonCredsSchemasGetAllInput + >(EventAnonCredsSchemasGetAll.token, { tenantId }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsSchemasGetAll.fromEvent(response); + + expect(eventInstance.instance).toEqual(expect.arrayContaining([])); + }); + + it(EventAnonCredsSchemasGetById.token, async () => { + const response$ = client.send< + EventAnonCredsSchemasGetById, + EventAnonCredsSchemasGetByIdInput + >(EventAnonCredsSchemasGetById.token, { tenantId, schemaId: 'some-id' }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsSchemasGetById.fromEvent(response); + + expect(eventInstance.instance).toEqual(null); + }); + + it(EventAnonCredsSchemasRegister.token, async () => { + const version = new Date().getTime().toString(); + const attributeNames = ['names', 'age']; + const name = 'my-schema'; + const response$ = client.send< + EventAnonCredsSchemasRegister, + EventAnonCredsSchemasRegisterInput + >(EventAnonCredsSchemasRegister.token, { + tenantId, + name, + version, + issuerDid, + attributeNames, + }); + + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsSchemasRegister.fromEvent(response); + + expect(eventInstance.instance).toMatchObject({ + attrNames: attributeNames, + issuerId: issuerDid, + name, + version, + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93ebdf1a54006abba74a71cf365a0f0661a32edf..0829affa99021eff4ffd7d9c6979028c77224ae7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -682,6 +682,9 @@ importers: apps/shared: dependencies: + '@aries-framework/anoncreds': + specifier: 0.4.2 + version: 0.4.2(expo@49.0.18)(react-native@0.72.7) '@aries-framework/core': specifier: 0.4.2 version: 0.4.2(expo@49.0.18)(react-native@0.72.7)