Skip to content
Snippets Groups Projects
Commit 9f9f628a authored by Steffen Schulze's avatar Steffen Schulze
Browse files

Merge branch 'credential-definitions' into 'main'

feat(ssi): credential definition module

See merge request eclipse/xfsc/ocm/ocm-engine!15
parents fc805a0a 547da0a0
No related branches found
No related tags found
No related merge requests found
Showing
with 456 additions and 5 deletions
import type { BaseEventInput } from './baseEvents.js';
import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
import { BaseEvent } from './baseEvents.js';
export type CredentialDefinitionWithId = AnonCredsCredentialDefinition & {
credentialDefinitionId: string;
};
export type EventAnonCredsCredentialDefinitionsGetAllInput = BaseEventInput;
export class EventAnonCredsCredentialDefinitionsGetAll extends BaseEvent<
Array<CredentialDefinitionWithId>
> {
public static token = 'anoncreds.credentialDefinitions.getAll';
public get instance() {
return this.data;
}
public static fromEvent(e: EventAnonCredsCredentialDefinitionsGetAll) {
return new EventAnonCredsCredentialDefinitionsGetAll(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
}
}
export type EventAnonCredsCredentialDefinitionsGetByIdInput = BaseEventInput<{
credentialDefinitionId: string;
}>;
export class EventAnonCredsCredentialDefinitionsGetById extends BaseEvent<CredentialDefinitionWithId | null> {
public static token = 'anoncreds.credentialDefinitions.getById';
public get instance() {
return this.data;
}
public static fromEvent(e: EventAnonCredsCredentialDefinitionsGetById) {
return new EventAnonCredsCredentialDefinitionsGetById(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
}
}
export type EventAnonCredsCredentialDefinitionsRegisterInput = BaseEventInput<{
schemaId: string;
tag: string;
issuerDid: string;
}>;
export class EventAnonCredsCredentialDefinitionsRegister extends BaseEvent<CredentialDefinitionWithId> {
public static token = 'anoncreds.credentialDefinitions.register';
public get instance() {
return this.data;
}
public static fromEvent(e: EventAnonCredsCredentialDefinitionsRegister) {
return new EventAnonCredsCredentialDefinitionsRegister(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
}
}
......@@ -8,3 +8,4 @@ export * from './events/connectionEvents.js';
export * from './events/didEvents.js';
export * from './events/tenantEvents.js';
export * from './events/schemaEvents.js';
export * from './events/credentialDefinitionEvents.js';
import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
import { Test } from '@nestjs/testing';
import { mockConfigModule } from '../../../config/__tests__/mockConfig.js';
import { AgentModule } from '../../agent.module.js';
import { CredentialDefinitionsController } from '../credentialDefinitions.controller.js';
import { CredentialDefinitionsService } from '../credentialDefinitions.service.js';
describe('CredentialDefinitionsController', () => {
let credentialDefinitionsController: CredentialDefinitionsController;
let credentialDefinitionsService: CredentialDefinitionsService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [mockConfigModule(), AgentModule],
controllers: [CredentialDefinitionsController],
providers: [CredentialDefinitionsService],
}).compile();
credentialDefinitionsService = moduleRef.get(CredentialDefinitionsService);
credentialDefinitionsController = moduleRef.get(
CredentialDefinitionsController,
);
});
describe('get all', () => {
it('should get all the registered credentialDefinitions of the agent', async () => {
const result: Array<AnonCredsCredentialDefinition> = [];
jest
.spyOn(credentialDefinitionsService, 'getAll')
.mockResolvedValue(result);
const event = await credentialDefinitionsController.getAll({
tenantId: 'some-id',
});
expect(event.data).toStrictEqual(result);
});
});
describe('get by id', () => {
it('should get a credentialDefinition by id', async () => {
const result: AnonCredsCredentialDefinition | null = null;
jest
.spyOn(credentialDefinitionsService, 'getById')
.mockResolvedValue(result);
const event = await credentialDefinitionsController.getById({
credentialDefinitionId: 'id',
tenantId: 'some-id',
});
expect(event.data).toStrictEqual(result);
});
});
describe('register credentialDefinition', () => {
it('should register a credentialDefinition on a ledger', async () => {
const result: AnonCredsCredentialDefinition = {
tag: 'some-tag',
type: 'CL',
issuerId: 'did:indy:issuer',
schemaId: 'schemaid:123:default',
value: {
primary: {},
},
};
jest
.spyOn(credentialDefinitionsService, 'register')
.mockResolvedValue(result);
const event = await credentialDefinitionsController.register({
tenantId: 'some-tenant-id',
tag: 'some-tag',
issuerDid: 'did:indy:issuer',
schemaId: 'schemaid:123:default',
});
expect(event.data).toStrictEqual(result);
});
});
});
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import {
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetAllInput,
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsGetByIdInput,
EventAnonCredsCredentialDefinitionsRegister,
EventAnonCredsCredentialDefinitionsRegisterInput,
} from '@ocm/shared';
import { CredentialDefinitionsService } from './credentialDefinitions.service.js';
@Controller('credentialDefinitions')
export class CredentialDefinitionsController {
public constructor(
private credentialDefinitionsService: CredentialDefinitionsService,
) {}
@MessagePattern(EventAnonCredsCredentialDefinitionsGetAll.token)
public async getAll(
options: EventAnonCredsCredentialDefinitionsGetAllInput,
): Promise<EventAnonCredsCredentialDefinitionsGetAll> {
return new EventAnonCredsCredentialDefinitionsGetAll(
await this.credentialDefinitionsService.getAll(options),
options.tenantId,
);
}
@MessagePattern(EventAnonCredsCredentialDefinitionsGetById.token)
public async getById(
options: EventAnonCredsCredentialDefinitionsGetByIdInput,
): Promise<EventAnonCredsCredentialDefinitionsGetById> {
return new EventAnonCredsCredentialDefinitionsGetById(
await this.credentialDefinitionsService.getById(options),
options.tenantId,
);
}
@MessagePattern(EventAnonCredsCredentialDefinitionsRegister.token)
public async register(
options: EventAnonCredsCredentialDefinitionsRegisterInput,
): Promise<EventAnonCredsCredentialDefinitionsRegister> {
return new EventAnonCredsCredentialDefinitionsRegister(
await this.credentialDefinitionsService.register(options),
options.tenantId,
);
}
}
import { Module } from '@nestjs/common';
import { AgentModule } from '../agent.module.js';
import { CredentialDefinitionsController } from './credentialDefinitions.controller.js';
import { CredentialDefinitionsService } from './credentialDefinitions.service.js';
@Module({
imports: [AgentModule],
providers: [CredentialDefinitionsService],
controllers: [CredentialDefinitionsController],
})
export class CredentialDefinitionsModule {}
import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
import type { IndyVdrRegisterCredentialDefinitionOptions } from '@aries-framework/indy-vdr';
import type {
EventAnonCredsCredentialDefinitionsGetAllInput,
EventAnonCredsCredentialDefinitionsGetByIdInput,
EventAnonCredsCredentialDefinitionsRegisterInput,
} from '@ocm/shared';
import { Injectable } from '@nestjs/common';
import { WithTenantService } from '../withTenantService.js';
@Injectable()
export class CredentialDefinitionsService {
public withTenantService: WithTenantService;
public constructor(withTenantService: WithTenantService) {
this.withTenantService = withTenantService;
}
public async getAll({
tenantId,
}: EventAnonCredsCredentialDefinitionsGetAllInput): Promise<
Array<AnonCredsCredentialDefinition>
> {
return this.withTenantService.invoke(tenantId, async (t) =>
(await t.modules.anoncreds.getCreatedCredentialDefinitions({})).map(
(r) => r.credentialDefinition,
),
);
}
public async getById({
tenantId,
credentialDefinitionId,
}: EventAnonCredsCredentialDefinitionsGetByIdInput): Promise<AnonCredsCredentialDefinition | null> {
return this.withTenantService.invoke(tenantId, async (t) => {
const { credentialDefinition } =
await t.modules.anoncreds.getCredentialDefinition(
credentialDefinitionId,
);
return credentialDefinition ?? null;
});
}
public async register({
tenantId,
schemaId,
issuerDid,
tag,
}: EventAnonCredsCredentialDefinitionsRegisterInput): Promise<
AnonCredsCredentialDefinition & { credentialDefinitionId: string }
> {
return this.withTenantService.invoke(tenantId, async (t) => {
const { credentialDefinitionState } =
await t.modules.anoncreds.registerCredentialDefinition<IndyVdrRegisterCredentialDefinitionOptions>(
{
credentialDefinition: {
issuerId: issuerDid,
type: 'CL',
schemaId,
tag,
},
options: {
endorserMode: 'internal',
endorserDid: issuerDid,
},
},
);
if (credentialDefinitionState.state !== 'finished') {
throw new Error(
`Error registering credentialDefinition: ${
credentialDefinitionState.state === 'failed'
? credentialDefinitionState.reason
: 'Not Finished'
}`,
);
}
return {
credentialDefinitionId:
credentialDefinitionState.credentialDefinitionId,
...credentialDefinitionState.credentialDefinition,
};
});
}
}
......@@ -7,7 +7,7 @@ import { AgentModule } from '../../agent.module.js';
import { SchemasController } from '../schemas.controller.js';
import { SchemasService } from '../schemas.service.js';
describe('ConnectionsController', () => {
describe('SchemassController', () => {
let schemasController: SchemasController;
let schemasService: SchemasService;
......
......@@ -42,7 +42,9 @@ export class SchemasService {
version,
issuerDid,
attributeNames,
}: EventAnonCredsSchemasRegisterInput): Promise<AnonCredsSchema> {
}: EventAnonCredsSchemasRegisterInput): Promise<
AnonCredsSchema & { schemaId: string }
> {
return this.withTenantService.invoke(tenantId, async (t) => {
const { schemaState } =
await t.modules.anoncreds.registerSchema<IndyVdrRegisterSchemaOptions>({
......@@ -53,7 +55,7 @@ export class SchemasService {
attrNames: attributeNames,
},
options: {
endorserMode: 'external',
endorserMode: 'internal',
endorserDid: issuerDid,
},
});
......@@ -66,7 +68,7 @@ export class SchemasService {
);
}
return schemaState.schema;
return { schemaId: schemaState.schemaId, ...schemaState.schema };
});
}
}
......@@ -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 { CredentialDefinitionsModule } from './agent/credentialDefinitions/credentialDefinitions.module.js';
import { SchemasModule } from './agent/schemas/schemas.module.js';
import { TenantsModule } from './agent/tenants/tenants.module.js';
import { config } from './config/config.js';
......@@ -21,6 +22,8 @@ import { validationSchema } from './config/validation.js';
}),
AgentModule,
ConnectionsModule,
SchemasModule,
CredentialDefinitionsModule,
DidsModule,
SchemasModule,
TenantsModule,
......
import type { INestApplication } from '@nestjs/common';
import type { ClientProxy } from '@nestjs/microservices';
import type {
EventAnonCredsCredentialDefinitionsGetAllInput,
EventAnonCredsCredentialDefinitionsGetByIdInput,
EventAnonCredsCredentialDefinitionsRegisterInput,
} from '@ocm/shared';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import {
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsRegister,
} from '@ocm/shared';
import { firstValueFrom } from 'rxjs';
import { AgentModule } from '../src/agent/agent.module.js';
import { CredentialDefinitionsModule } from '../src/agent/credentialDefinitions/credentialDefinitions.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 { SchemasService } from '../src/agent/schemas/schemas.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';
describe('CredentialDefinitions', () => {
const TOKEN = 'CREDENTIAL_DEFINITIONS_CLIENT_SERVICE';
let app: INestApplication;
let client: ClientProxy;
let tenantId: string;
let issuerDid: string;
let schemaId: string;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
mockConfigModule(3004, true),
AgentModule,
SchemasModule,
CredentialDefinitionsModule,
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 tenantsService = app.get(TenantsService);
const { id } = await tenantsService.create(TOKEN);
tenantId = id;
const didsService = app.get(DidsService);
const [did] = await didsService.registerDidIndyFromSeed({
tenantId,
seed: '12312367897123300000000000000000',
});
issuerDid = did;
const schemaService = app.get(SchemasService);
const { schemaId: sid } = await schemaService.register({
issuerDid,
tenantId,
name: 'test-schema-name',
version: `1.${new Date().getTime()}`,
attributeNames: ['none'],
});
schemaId = sid;
});
afterAll(async () => {
await app.close();
client.close();
});
it(EventAnonCredsCredentialDefinitionsGetAll.token, async () => {
const response$ = client.send<
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetAllInput
>(EventAnonCredsCredentialDefinitionsGetAll.token, { tenantId });
const response = await firstValueFrom(response$);
const eventInstance =
EventAnonCredsCredentialDefinitionsGetAll.fromEvent(response);
expect(eventInstance.instance).toEqual(expect.arrayContaining([]));
});
it(EventAnonCredsCredentialDefinitionsGetById.token, async () => {
const response$ = client.send<
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsGetByIdInput
>(EventAnonCredsCredentialDefinitionsGetById.token, {
tenantId,
credentialDefinitionId: 'some-id',
});
const response = await firstValueFrom(response$);
const eventInstance =
EventAnonCredsCredentialDefinitionsGetById.fromEvent(response);
expect(eventInstance.instance).toEqual(null);
});
it(EventAnonCredsCredentialDefinitionsRegister.token, async () => {
const tag = `tag:${new Date().getTime()}`;
const response$ = client.send<
EventAnonCredsCredentialDefinitionsRegister,
EventAnonCredsCredentialDefinitionsRegisterInput
>(EventAnonCredsCredentialDefinitionsRegister.token, {
tenantId,
schemaId,
issuerDid,
tag,
});
const response = await firstValueFrom(response$);
const eventInstance =
EventAnonCredsCredentialDefinitionsRegister.fromEvent(response);
expect(eventInstance.instance).toMatchObject({
schemaId,
tag,
issuerId: issuerDid,
type: 'CL',
});
});
});
......@@ -92,7 +92,7 @@ describe('Schemas', () => {
});
it(EventAnonCredsSchemasRegister.token, async () => {
const version = new Date().getTime().toString();
const version = `1.${new Date().getTime()}`;
const attributeNames = ['names', 'age'];
const name = 'my-schema';
const response$ = client.send<
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment