Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • eclipse/xfsc/ocm/ocm-engine
  • zdravko61/ocm-engine
  • mjuergenscg/ocm-engine
  • tsabolov/ocm-engine
  • mikesell/ocm-engine
5 results
Show changes
Showing
with 0 additions and 1807 deletions
import type { Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
@Injectable()
export default class CredentialRepository {
public constructor(private readonly prismaService: PrismaService) {}
public async createCredential(data: Prisma.CredentialCreateInput) {
const credential = await this.prismaService.credential.create({ data });
const credDef = await this.prismaService.credentialDef.findFirst({
where: { credDefId: data.credDefId },
});
if (!credDef) {
return credential;
}
await this.prismaService.credentialDef.update({
where: {
credDefId: data.credDefId,
},
data: {
credentials: {
connect: {
id: credential.id,
},
},
},
});
return credential;
}
public async findUniqueCredential(params: {
where: Prisma.CredentialWhereUniqueInput;
}) {
const { where } = params;
return this.prismaService.credential.findUnique({
where,
});
}
public async updateCredential(params: {
where: Prisma.CredentialWhereUniqueInput;
data: Prisma.CredentialUpdateInput;
}) {
const { where, data } = params;
return this.prismaService.credential.update({
data,
where,
});
}
public async findCredential(params: {
skip?: number;
take?: number;
cursor?: Prisma.CredentialWhereUniqueInput;
where?: Prisma.CredentialWhereInput;
orderBy?: Prisma.CredentialOrderByWithRelationInput;
}) {
const { skip, take, cursor, where, orderBy } = params;
return this.prismaService.$transaction([
this.prismaService.credential.count({
where: {
...where,
},
}),
this.prismaService.credential.findMany({
skip,
take,
cursor,
where,
orderBy,
}),
]);
}
public async deleteCredential(params: {
where: Prisma.CredentialWhereUniqueInput;
}) {
const { where } = params;
return this.prismaService.credential.delete({
where,
});
}
}
import type { Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
@Injectable()
export default class CredentialsTypeRepository {
public constructor(private readonly prismaService: PrismaService) {}
public async createCredentialsType(data: Prisma.CredentialsTypeCreateInput) {
return this.prismaService.credentialsType.create({ data });
}
public async createOrUpdateCredentialsType(
data: Prisma.CredentialsTypeCreateInput,
) {
const credentialType = await this.prismaService.credentialsType.findFirst({
where: {
type: {
equals: data.type,
mode: 'insensitive',
},
},
});
if (credentialType) {
return this.prismaService.credentialsType.update({
where: {
id: credentialType.id,
},
data,
});
}
const credentialTypeSchemaCheck =
await this.prismaService.credentialsType.findFirst({
where: {
schemaId: {
equals: data.schemaId,
},
},
});
if (credentialTypeSchemaCheck) {
return this.prismaService.credentialsType.update({
where: {
id: credentialTypeSchemaCheck.id,
},
data,
});
}
return this.prismaService.credentialsType.create({
data,
});
}
public async findUniqueCredentialsType(data: { type: string }) {
return this.prismaService.credentialsType.findFirst({
where: {
type: {
equals: data.type,
mode: 'insensitive',
},
},
});
}
// TODO check
public async updateCredentialsType(params: {
where: Prisma.CredentialsTypeWhereUniqueInput;
data: Prisma.CredentialsTypeUpdateInput;
}) {
const { where, data } = params;
return this.prismaService.credentialsType.update({
data,
where,
});
}
public async findCredentialsType(params: {
skip?: number;
take?: number;
cursor?: Prisma.CredentialsTypeWhereUniqueInput;
where?: Prisma.CredentialsTypeWhereInput;
orderBy?: Prisma.CredentialsTypeOrderByWithRelationInput;
}) {
const { skip, take, cursor, where, orderBy } = params;
return this.prismaService.$transaction([
this.prismaService.credentialsType.count({
where: {
...where,
},
}),
this.prismaService.credentialsType.findMany({
skip,
take,
cursor,
where,
orderBy,
}),
]);
}
}
import type CredentialDto from '../entities/credential.entity.js';
import type CredentialTypeDto from '../entities/credentialType.entity.js';
import type OfferCredentialDto from '../entities/entity.js';
import type GetIssueCredentialsDto from '../entities/get-issue-credentials.dto.js';
import type ProposeCredentialDto from '../entities/propose-credential.dto.js';
import type { Credential, Prisma } from '@prisma/client';
import {
BadRequestException,
Injectable,
PreconditionFailedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import TSAClientService from '../../client/tsa.client.js';
import { TSAService } from '../../common/constants.js';
import CredentialDefService from '../../credentialDef/services/service.js';
import PrismaService from '../../prisma/prisma.service.js';
import Utils from '../../utils/common.js';
import logger from '../../utils/logger.js';
import pagination from '../../utils/pagination.js';
import CredentialRepository from '../repository/credential.repository.js';
import CredentialsTypeRepository from '../repository/credentialType.repository.js';
@Injectable()
export default class AttestationService {
private credentialRepository: CredentialRepository;
private credentialRepositoryType: CredentialsTypeRepository;
public constructor(
private readonly credDefService: CredentialDefService,
private readonly prismaService: PrismaService,
private readonly restClient: RestClientService,
private readonly natsClient: NatsClientService,
private readonly tsaClient: TSAClientService,
private readonly configService: ConfigService,
) {
this.credentialRepository = new CredentialRepository(this.prismaService);
this.credentialRepositoryType = new CredentialsTypeRepository(
this.prismaService,
);
}
public static readonly status = {
OFFER_SENT: 'offer-sent',
PROPOSAL_SENT: 'proposal-sent',
REQUEST_RECEIVED: 'request-received',
DONE: 'done',
OFFER_RECEIVED: 'offer-received',
};
public static readonly principalMemberCredential =
'principalMemberCredential';
public static readonly connectionStatus = {
TRUSTED: 'trusted',
};
public async createOfferCredential(
credentialRequest: OfferCredentialDto,
isTrustedConnectionRequired = false,
) {
// TODO is it a correct conditions here? Should not be just isTrustedConnectionRequired?
if (!isTrustedConnectionRequired) {
const connection = await this.getConnectionByID(
credentialRequest.connectionId,
);
logger.info(`connection ${JSON.stringify(connection)}`);
if (connection?.status !== AttestationService.connectionStatus.TRUSTED) {
return null;
}
}
const agentUrl = this.configService.get('agent.AGENT_URL');
const credentialRequestObj = { ...credentialRequest };
const credDef = await this.findCredDef(
credentialRequestObj.credentialDefinitionId,
);
const expirationDate = Utils.calculateExpiry(credDef.expiryHours);
if (expirationDate) {
credentialRequestObj.attributes.push({
name: 'expirationDate',
value: expirationDate.toString(),
});
}
const schemaDetails = await this.getSchemaAndAttributesBySchemaIDFromLedger(
credDef.schemaID,
);
logger.info(
`schemaDetails?.attrNames?.length ${schemaDetails?.attrNames?.length}`,
);
logger.info(
`credentialRequest.preview.attributes.length ${credentialRequest.attributes.length}`,
);
if (
schemaDetails?.attrNames?.length !== credentialRequest.attributes.length
) {
throw new BadRequestException('Invalid attributes');
}
logger.info(`offer-credential payload: ${credentialRequestObj}`);
try {
const credentialRequestPayload = {
connectionId: credentialRequestObj.connectionId,
credentialDefinitionId: credentialRequestObj.credentialDefinitionId,
comment: credentialRequestObj.comment,
preview: {
'@type':
'https://didcomm.org/issue-credential/1.0/credential-preview',
attributes: credentialRequestObj.attributes,
},
autoAcceptCredential: credentialRequestObj.autoAcceptCredential,
};
logger.info(
`***Offer Credential Payload*** ${JSON.stringify(
credentialRequestPayload,
)}`,
);
const responseData = await this.restClient.post(
`${agentUrl}/credentials/offer-credential`,
credentialRequestPayload,
);
logger.info(responseData);
return responseData;
} catch (error) {
logger.error(JSON.stringify(error));
throw new Error(JSON.stringify(error));
}
}
public async proposeCredential(connectionCreate: ProposeCredentialDto) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const connectionCreateObj = { ...connectionCreate };
try {
const responseData = await this.restClient.post(
`${agentUrl}/credentials/propose-credential`,
connectionCreateObj,
);
logger.info(responseData);
return responseData;
} catch (error) {
logger.error(JSON.stringify(error));
throw new Error(JSON.stringify(error));
}
}
public async acceptRequestCredential(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.post(
`${agentUrl}/credentials/${credentialId}/accept-request`,
{},
);
logger.info(responseData);
return responseData;
}
public async acceptProposeCredential(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.post(
`${agentUrl}/credentials/${credentialId}/accept-proposal`,
{},
);
logger.info(responseData);
return responseData;
}
public async acceptCredentialOffer(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.post(
`${agentUrl}/credentials/${credentialId}/accept-offer`,
{},
);
logger.info(responseData);
return responseData;
}
public async acceptCredential(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.post(
`${agentUrl}/credentials/${credentialId}/accept-credential`,
{},
);
logger.info(responseData);
return responseData;
}
public async createCredential(credential: CredentialDto) {
const connection = await this.getConnectionByID(credential.connectionId);
const credDef = await this.findCredDef(credential.credDefId);
logger.info(`credDef.expiryHours ${credDef.expiryHours}`);
const expirationDate = Utils.calculateExpiry(credDef.expiryHours);
logger.info(`expirationDate ${expirationDate}`);
const tempCredential = credential;
delete tempCredential.schemaId;
return this.credentialRepository.createCredential({
...tempCredential,
...(expirationDate !== 'NA' && { expirationDate }),
principalDid: connection.theirDid,
});
}
public async getConnectionByID(connectionID: string) {
const connection = await this.natsClient.getConnectionById(connectionID);
return connection;
}
public async updateCredential(credential: CredentialDto) {
return this.credentialRepository.updateCredential({
where: { credentialId: credential.credentialId },
data: {
state: credential.state,
updatedDate: new Date(),
},
});
}
public findCredentialById(credentialId: string) {
const where: Prisma.CredentialWhereUniqueInput = { credentialId };
return this.credentialRepository.findUniqueCredential({ where });
}
public findCredentialByThreadId(threadId: string) {
const where: Prisma.CredentialWhereUniqueInput = { threadId };
return this.credentialRepository.findUniqueCredential({ where });
}
public async findCredential(
pageSize: number,
page: number,
isReceived: boolean,
state?: string | false,
credDefId?: string | false,
createdDateStart?: string | false,
createdDateEnd?: string | false,
updatedDateStart?: string | false,
updatedDateEnd?: string | false,
expirationDateStart?: string | false,
expirationDateEnd?: string | false,
connectionId?: string | false,
principalDid?: string | false,
) {
let query: {
skip?: number;
take?: number;
cursor?: Prisma.CredentialWhereUniqueInput;
where: Prisma.CredentialWhereInput;
orderBy?: Prisma.CredentialOrderByWithRelationInput;
} = {
where: {},
};
if (state) {
const states: string[] = state.split(',');
query.where.state = { in: states };
}
if (credDefId) {
query.where.credDefId = credDefId;
}
if (createdDateStart) {
query.where.createdDate = { gte: createdDateStart };
}
if (createdDateEnd) {
query.where.createdDate = Object.assign({}, query.where.createdDate, {
lte: createdDateEnd,
});
}
if (updatedDateStart) {
query.where.updatedDate = { gte: updatedDateStart };
}
if (updatedDateEnd) {
query.where.updatedDate = Object.assign({}, query.where.updatedDate, {
lte: updatedDateEnd,
});
}
if (expirationDateStart) {
query.where.expirationDate = { gte: expirationDateStart };
}
if (expirationDateEnd) {
query.where.expirationDate = Object.assign(
{},
query.where.expirationDate,
{ lte: expirationDateEnd },
);
}
if (connectionId) {
query.where.connectionId = connectionId;
}
if (principalDid) {
query.where.principalDid = principalDid;
}
if (isReceived) {
// TODO we need to check the case when first and second OCMs can re-use the same connection
// and can issue credentials to each other. Will this function returns correct results for
// every OCM?
const receivedConnections =
await this.natsClient.getReceivedConnections();
if (
Array.isArray(receivedConnections) &&
receivedConnections.length > 0
) {
const receivedConnectionIds = receivedConnections.map(
(connection) => connection.connectionId,
);
query.where.connectionId = { in: receivedConnectionIds };
}
}
query = { ...query, ...pagination(pageSize, page) };
return this.credentialRepository.findCredential(query);
}
public async issueMemberCredentials(data: {
status: string;
connectionId: string;
theirLabel: string;
participantDID: string;
theirDid: string;
credDefId: string;
attributes: { name: string; value: string }[];
autoAcceptCredential: string;
}) {
logger.info(JSON.stringify(data));
const payload: OfferCredentialDto = {
connectionId: data.connectionId,
credentialDefinitionId: data.credDefId,
comment: 'Created',
attributes: data.attributes,
autoAcceptCredential: data.autoAcceptCredential,
};
logger.info(JSON.stringify(payload));
const tsaResponse = await this.tsaClient.getPolicy(
`${this.configService.get('TSA_URL')}/${
TSAService.PRINCIPAL_CREDENTIAL_REQUEST
}/1.0/evaluation`,
);
if (tsaResponse?.success && !tsaResponse.returnData) {
throw new PreconditionFailedException('TSA ERROR!');
}
const result = await this.createOfferCredential(payload, true);
logger.info(JSON.stringify(result));
return result;
}
public getPrincipalMemberShipCredentials(data: { type: string }) {
return this.credentialRepositoryType.findUniqueCredentialsType(data);
}
public async getSchemaAndAttributesBySchemaIDFromLedger(schemaID: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.get(
`${agentUrl}/schemas/${schemaID}`,
);
if (!responseData?.id) {
throw new BadRequestException('Invalid schema ID');
}
return responseData;
}
public updateSchemaByType(type: string, body: { schemaId: string }) {
return this.credentialRepositoryType.updateCredentialsType({
where: {
type,
},
data: {
schemaId: body.schemaId,
},
});
}
public async getIssueCredentials(data: GetIssueCredentialsDto) {
return this.credentialRepository.findCredential({
where: {
connectionId: data.connectionId,
},
});
}
public async getCredentialInformation(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.get(
`${agentUrl}/credentials/${credentialId}`,
);
if (!responseData?.id) {
throw new BadRequestException('Invalid credential ID');
}
return responseData;
}
public async deleteCredential(credentialId: string) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.delete(
`${agentUrl}/credentials/${credentialId}`,
);
await this.credentialRepository.deleteCredential({
where: {
credentialId,
},
});
return responseData;
}
public createCredentialsType(credentialType: CredentialTypeDto) {
return this.credentialRepositoryType.createCredentialsType({
type: credentialType.type,
schemaId: credentialType.schemaId,
});
}
public connectionTrusted(connectionId: string) {
return this.natsClient.connectionTrusted(connectionId);
}
public async findCredDef(credentialDefinitionId: string) {
const credDefRes = await this.credDefService.findCredentialDefById(
credentialDefinitionId,
);
if (!credDefRes[0]) {
return {
expiryHours: '-1',
schemaID: '',
};
}
return credDefRes[1][0];
}
public async findReceivedCredentials() {
try {
let result: Credential[] = [];
const receivedConnections =
await this.natsClient.getReceivedConnections();
if (
Array.isArray(receivedConnections) &&
receivedConnections.length > 0
) {
const receivedConnectionIds = receivedConnections.map(
(connection) => connection.connectionId,
);
const credentials = await this.credentialRepository.findCredential({
where: { connectionId: { in: receivedConnectionIds } },
});
[, result] = credentials;
}
return result;
} catch (error) {
logger.error(JSON.stringify(error));
throw new Error(JSON.stringify(error));
}
}
}
import schemaAgentDto from '../../../schemas/tests/stubs/schema-from-agent-dto.js';
import credentialDto from '../stubs/credential-dto.js';
import credentialTypeDto from '../stubs/credential-type-dto.js';
const AttestationServiceMock = jest.fn().mockReturnValue({
createOfferCredential: jest.fn().mockReturnValue({}),
acceptRequestCredential: jest.fn().mockReturnValue({}),
findCredentialById: jest.fn().mockReturnValue(credentialDto()),
createCredentialsType: jest.fn().mockReturnValue(credentialTypeDto()),
getPrincipalMemberShipCredentials: jest
.fn()
.mockReturnValue(credentialTypeDto()),
createCredential: jest.fn().mockReturnValue(credentialDto()),
updateCredential: jest.fn().mockReturnValue(credentialDto()),
issueMemberCredentials: jest.fn().mockReturnValue({}),
getSchemaAndAttributesBySchemaIDFromLedger: jest
.fn()
.mockReturnValue(schemaAgentDto),
});
export default AttestationServiceMock;
import type { ResponseType } from '../../common/response.js';
import type CredentialDto from '../entities/credential.entity.js';
import type CredentialStateDto from '../entities/credential.state.entity.js';
import type CredentialTypeDto from '../entities/credentialType.entity.js';
import type GetCredentialParams from '../entities/get.credential.params.js';
import type GetCredentialQuery from '../entities/get.credential.query.js';
import type { TestingModule } from '@nestjs/testing';
import type { Response } from 'express';
import { HttpStatus } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import { createResponse } from 'node-mocks-http';
import { NATSServices } from '../../common/constants.js';
import CredentialDefService from '../../credentialDef/services/service.js';
import CredentialDefServiceMock from '../../credentialDef/tests/__mocks__/service.js';
import SchemasService from '../../schemas/services/service.js';
import SchemasServiceMock from '../../schemas/tests/__mocks__/service.js';
import UserInfoService from '../../userInfo/services/service.js';
import UserInfoServiceMock from '../../userInfo/tests/__mocks__/service.js';
import AttestationController from '../controller/controller.js';
import AttestationService from '../services/service.js';
import AttestationServiceMock from './__mocks__/service.js';
import credentialDto from './stubs/credential-dto.js';
import credentialStateDto from './stubs/credential-state-dto.js';
import credentialTypeDto from './stubs/credential-type-dto.js';
describe('AttestationController', () => {
let attestationController: AttestationController;
let attestationService: AttestationService;
beforeEach(async () => {
const AttestationServiceProvider = {
provide: AttestationService,
useFactory: AttestationServiceMock,
};
const CredentialDefServiceProvider = {
provide: CredentialDefService,
useFactory: CredentialDefServiceMock,
};
const UserInfoServiceProvider = {
provide: UserInfoService,
useFactory: UserInfoServiceMock,
};
const SchemasServiceProvider = {
provide: SchemasService,
useFactory: SchemasServiceMock,
};
const module: TestingModule = await Test.createTestingModule({
imports: [
ClientsModule.register([
{
name: NATSServices.SERVICE_NAME,
transport: Transport.NATS,
},
]),
],
controllers: [AttestationController],
providers: [
AttestationServiceProvider,
CredentialDefServiceProvider,
UserInfoServiceProvider,
SchemasServiceProvider,
ConfigService,
],
}).compile();
attestationController = module.get<AttestationController>(
AttestationController,
);
attestationService = module.get<AttestationService>(AttestationService);
jest.clearAllMocks();
});
it('should be defined', () => {
expect(attestationController).toBeDefined();
});
// describe('createOfferCredential()', () => {
// let attestationResponse: Response<string, Record<string, any>>;
// beforeEach(async () => {
// const response = httpMocks.createResponse();
// attestationResponse = await attestationController.createOfferCredential(
// offerCredentialDto(),
// response,
// );
// });
// it('should call createOfferCredential() from service', async () => {
// expect(attestationService.createOfferCredential).toHaveBeenCalled();
// });
// it('should retrieve created credential', async () => {
// expect(
// attestationService.createOfferCredential(offerCredentialDto()),
// ).not.toBe(null);
// });
// it(`should retrieve HTTP status created(${HttpStatus.CREATED})`, async () => {
// expect(attestationResponse?.statusCode).toEqual(HttpStatus.CREATED);
// });
// });
describe('acceptOfferCredential()', () => {
let attestationResponse: ResponseType;
let query: { credentialId: string };
beforeEach(async () => {
query = {
credentialId: credentialDto().credentialId,
};
attestationResponse =
await attestationController.acceptOfferCredential(query);
});
it('should call acceptRequestCredential() from service', async () => {
expect(attestationService.acceptRequestCredential).toHaveBeenCalled();
});
it('should retrieve created credential', async () => {
expect(
attestationService.acceptRequestCredential(
query.credentialId ? query.credentialId : '',
),
).not.toBe(null);
});
it(`should retrieve HTTP status accepted(${HttpStatus.ACCEPTED})`, async () => {
expect(attestationResponse?.statusCode).toEqual(HttpStatus.ACCEPTED);
});
});
describe('webHookCredentials()', () => {
let attestationResponse: ResponseType;
let body: { credentialRecord: CredentialStateDto };
let credentialObj: CredentialDto;
beforeEach(async () => {
body = { credentialRecord: credentialStateDto() };
credentialObj = {
credentialId: body.credentialRecord.id,
state: body.credentialRecord.state,
connectionId: body.credentialRecord.connectionId,
credDefId:
body.credentialRecord.metadata['_internal/indyCredential']
.credentialDefinitionId,
threadId: body.credentialRecord.threadId,
};
attestationResponse =
await attestationController.webHookCredentials(body);
});
it('should call createCredential() from service', async () => {
expect(attestationService.createCredential).toHaveBeenCalled();
});
it('should retrieve created credential', async () => {
expect(attestationService.createCredential(credentialObj)).toEqual(
credentialDto(),
);
});
it(`should retrieve HTTP status accepted(${HttpStatus.CREATED})`, async () => {
expect(attestationResponse?.statusCode).toEqual(HttpStatus.CREATED);
});
});
describe('getCredential()', () => {
let attestationResponse: Response<string, Record<string, unknown>>;
let params: GetCredentialParams;
let query: GetCredentialQuery;
beforeEach(async () => {
params = { id: credentialDto().credentialId };
query = {
state: credentialDto().state,
};
const response = createResponse();
attestationResponse = await attestationController.getCredential(
params,
query,
response,
);
});
it('should call findCredentialById() from service', async () => {
expect(attestationService.findCredentialById).toHaveBeenCalled();
});
it('should retrieve credential', async () => {
expect(attestationService.findCredentialById(params.id)).toEqual(
credentialDto(),
);
});
it(`should retrieve HTTP status OK(${HttpStatus.OK})`, async () => {
expect(attestationResponse?.statusCode).toEqual(HttpStatus.OK);
});
});
// describe('offerMemberShipCredentials()', () => {
// let data: {
// status: string;
// connectionId: string;
// theirLabel: string;
// participantDID: string;
// theirDid: string;
// };
// let attestationResponse: ResponseType;
// beforeEach(async () => {
// data = {
// status: 'status',
// connectionId: credentialDto().connectionId,
// theirLabel: credentialDto().principalDid || '',
// participantDID,
// theirDid: credentialDto().principalDid || '',
// };
// attestationResponse =
// await attestationController.offerMemberShipCredentials(data);
// });
// it('should call issueMemberCredentials() from service', async () => {
// expect(attestationService.issueMemberCredentials).toHaveBeenCalled();
// });
// it('should retrieve created credential', async () => {
// expect(
// attestationService.issueMemberCredentials({
// ...data,
// credDefId: credentialDto().credDefId,
// attributes: offerCredentialDto().attributes,
// autoAcceptCredential: offerCredentialDto().autoAcceptCredential,
// }),
// ).not.toBe(null);
// });
// it(`should retrieve HTTP status accepted(${HttpStatus.OK})`, async () => {
// expect(attestationResponse?.statusCode).toEqual(HttpStatus.OK);
// });
// });
describe('createCredentialType()', () => {
let attestationResponse: ResponseType;
let body: CredentialTypeDto;
beforeEach(async () => {
body = credentialTypeDto();
attestationResponse =
await attestationController.createCredentialType(body);
});
it('should call createCredentialsType() from service', async () => {
expect(attestationService.createCredentialsType).toHaveBeenCalled();
});
it('should retrieve created updated credential-type', async () => {
expect(attestationService.createCredentialsType(body)).toEqual(
credentialTypeDto(),
);
});
it(`should retrieve HTTP status created(${HttpStatus.CREATED})`, async () => {
expect(attestationResponse?.statusCode).toEqual(HttpStatus.CREATED);
});
});
// describe('getCredential()', () => {
// let attestationResponse: Response<string, Record<string, any>>;
// let query: { credentialId: string; participantId: string };
// beforeEach(async () => {
// query = {
// credentialId: credentialDto().credentialId,
// participantId: credentialDto().participantId || '',
// };
// attestationResponse = await attestationController.acceptOfferCredential(query);
// });
// });
});
import type { TestingModule } from '@nestjs/testing';
import { HttpModule } from '@nestjs/axios';
import { Test } from '@nestjs/testing';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import NatsClientServiceMock from '../../client/tests/__mocks__/nats.client.js';
import RestClientServiceMock from '../../client/tests/__mocks__/rest.client.js';
import CredentialDefService from '../../credentialDef/services/service.js';
import CredentialDefServiceMock from '../../credentialDef/tests/__mocks__/service.js';
import PrismaService from '../../prisma/prisma.service.js';
import PrismaServiceMock from '../../prisma/tests/__mocks__/prisma.service.js';
import AttestationModule from '../module.js';
import AttestationService from '../services/service.js';
import AttestationServiceMock from './__mocks__/service.js';
describe('AttestationModule', () => {
let attestationModule: AttestationModule;
const AttestationServiceProvider = {
provide: AttestationService,
useFactory: AttestationServiceMock,
};
const CredentialDefServiceProvider = {
provide: CredentialDefService,
useFactory: CredentialDefServiceMock,
};
const PrismaServiceProvider = {
provide: PrismaService,
useFactory: PrismaServiceMock,
};
const NatsClientServiceProvider = {
provide: NatsClientService,
useFactory: NatsClientServiceMock,
};
const RestClientServiceProvider = {
provide: RestClientService,
useFactory: RestClientServiceMock,
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [HttpModule],
providers: [
AttestationModule,
AttestationServiceProvider,
CredentialDefServiceProvider,
PrismaServiceProvider,
NatsClientServiceProvider,
RestClientServiceProvider,
],
}).compile();
attestationModule = module.get<AttestationModule>(AttestationModule);
});
it('should be defined', () => {
expect(attestationModule).toBeDefined();
});
});
import type { ResponseType } from '../../common/response.js';
import type { TestingModule } from '@nestjs/testing';
import type { Credential, CredentialsType } from '@prisma/client';
import { HttpModule } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { Test } from '@nestjs/testing';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import NatsClientServiceMock from '../../client/tests/__mocks__/nats.client.js';
import RestClientServiceMock from '../../client/tests/__mocks__/rest.client.js';
import TSAClientServiceMock from '../../client/tests/__mocks__/tsa.client.js';
import { natsConnectionResponse } from '../../client/tests/stubs/nats-response.js';
import TSAClientService from '../../client/tsa.client.js';
import CredentialDefService from '../../credentialDef/services/service.js';
import CredentialDefServiceMock from '../../credentialDef/tests/__mocks__/service.js';
import PrismaService from '../../prisma/prisma.service.js';
import PrismaServiceMock from '../../prisma/tests/__mocks__/prisma.service.js';
import AttestationService from '../services/service.js';
import credentialDto from './stubs/credential-dto.js';
import credentialsTypeDto from './stubs/credential-type-dto.js';
describe('AttestationService', () => {
let attestationService: AttestationService;
let spyGetConnectionByID: jest.SpyInstance;
const CredentialDefServiceProvider = {
provide: CredentialDefService,
useFactory: CredentialDefServiceMock,
};
const PrismaServiceProvider = {
provide: PrismaService,
useFactory: PrismaServiceMock,
};
const NatsClientServiceProvider = {
provide: NatsClientService,
useFactory: NatsClientServiceMock,
};
const RestClientServiceProvider = {
provide: RestClientService,
useFactory: RestClientServiceMock,
};
const TSAClientServiceProvider = {
provide: TSAClientService,
useFactory: TSAClientServiceMock,
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [HttpModule],
providers: [
AttestationService,
CredentialDefServiceProvider,
PrismaServiceProvider,
RestClientServiceProvider,
NatsClientServiceProvider,
TSAClientServiceProvider,
ConfigService,
],
}).compile();
attestationService = module.get<AttestationService>(AttestationService);
jest.clearAllMocks();
});
it('should be defined', () => {
expect(attestationService).toBeDefined();
});
// describe('createOfferCredential()', () => {
// let attestationResponse: ResponseType;
// beforeEach(async () => {
// jest
// .spyOn(attestationService, 'getSchemaAndAttributesBySchemaIDFromLedger')
// .mockImplementation(
// AttestationServiceMock().getSchemaAndAttributesBySchemaIDFromLedger,
// );
// attestationResponse = await attestationService.createOfferCredential(
// offerCredentialDto(),
// );
// });
// it('should call post() from restClient', async () => {
// expect(RestClientServiceMock().post).toHaveBeenCalled();
// });
// it('should get a response from AFJ', async () => {
// expect(attestationResponse).not.toBe(null);
// });
// });
describe('acceptRequestCredential()', () => {
let attestationResponse: ResponseType;
beforeEach(async () => {
attestationResponse = await attestationService.acceptRequestCredential(
credentialDto().credentialId,
);
});
it('should call post() from restClient', async () => {
expect(RestClientServiceMock().post).toHaveBeenCalled();
});
it('should get a response from AFJ', async () => {
expect(attestationResponse).not.toBe(null);
});
});
describe('createCredential()', () => {
beforeEach(async () => {
spyGetConnectionByID = jest.spyOn(
attestationService,
'getConnectionByID',
);
await attestationService.createCredential(credentialDto());
});
it('should call getConnectionByID()', async () => {
expect(spyGetConnectionByID).toHaveBeenCalled();
});
it('should call create() from PrismaService.credential', async () => {
expect(PrismaServiceMock().credential.create).toHaveBeenCalled();
});
});
describe('getConnectionByID()', () => {
let attestationResponse: ResponseType;
beforeEach(async () => {
attestationResponse = await attestationService.getConnectionByID(
credentialDto().connectionId,
);
});
it('should call getConnectionById() from NatsClientService', async () => {
expect(NatsClientServiceMock().getConnectionById).toHaveBeenCalled();
});
it('should receive connection details from NatsClientService', async () => {
expect(attestationResponse).toEqual(natsConnectionResponse);
});
});
describe('updateCredential()', () => {
let attestationResponse: Credential;
beforeEach(async () => {
attestationResponse =
await attestationService.updateCredential(credentialDto());
});
it('should call update() from PrismaService.credential', async () => {
expect(PrismaServiceMock().credential.update).toHaveBeenCalled();
});
it('should retreive updated credential', async () => {
expect(attestationResponse).toEqual(credentialDto());
});
});
describe('findCredentialById()', () => {
let attestationResponse: Credential | null;
let id: string;
beforeEach(async () => {
id = credentialDto().credDefId;
attestationResponse = await attestationService.findCredentialById(id);
});
it('should call findUnique() from PrismaService.credential', async () => {
expect(PrismaServiceMock().credential.findUnique).toHaveBeenCalled();
});
it('should retrieve schema by ID', async () => {
expect(attestationResponse).toEqual(credentialDto());
});
});
describe('findCredential()', () => {
let attestationResponse: Array<number | Credential[]>;
beforeEach(async () => {
attestationResponse = await attestationService.findCredential(
10,
10,
false,
false,
false,
false,
false,
false,
false,
false,
false,
);
});
it('should call findMany() from PrismaService.credential', async () => {
expect(PrismaServiceMock().credential.findMany).toHaveBeenCalled();
});
it('should call count() from PrismaService.credential', async () => {
expect(PrismaServiceMock().credential.count).toHaveBeenCalled();
});
it('should call $transaction() from PrismaService', async () => {
expect(PrismaServiceMock().$transaction).toHaveBeenCalled();
});
it('should retrieve schema by ID', async () => {
expect(attestationResponse).toEqual([1, [credentialDto()]]);
});
});
// describe('issueMemberCredentials()', () => {
// let attestationResponse: any;
// beforeEach(async () => {
// spyCreateOfferCredential = jest.spyOn(
// attestationService,
// 'createOfferCredential',
// );
// attestationResponse = await attestationService.issueMemberCredentials({
// status: credentialDto().state,
// connectionId: credentialDto().connectionId,
// theirLabel: credentialDto().principalDid || '',
// participantDID,
// credDefId: credentialDto().credDefId,
// theirDid: credentialDto().principalDid || '',
// attributes: [
// { name: 'foo', value: 'bar' },
// { name: 'foo', value: 'bar' },
// { name: 'foo', value: 'bar' },
// ],
// autoAcceptCredential: 'never',
// });
// });
// it('should call createOfferCredential()', async () => {
// expect(spyCreateOfferCredential).toHaveBeenCalled();
// });
// it('should get a response from AFJ', async () => {
// expect(attestationResponse).not.toBe(null);
// });
// });
describe('getPrincipalMemberShipCredentials()', () => {
let attestationResponse: CredentialsType | null;
let data: {
type: string;
};
beforeEach(async () => {
data = { type: credentialsTypeDto().type };
attestationResponse =
await attestationService.getPrincipalMemberShipCredentials(data);
});
it('should call findUnique() from PrismaService.credentialsType', async () => {
expect(PrismaServiceMock().credentialsType.findFirst).toHaveBeenCalled();
});
it('should get principal member credentials type', async () => {
expect(attestationResponse).toEqual(credentialsTypeDto());
});
});
describe('createCredentialsType()', () => {
let attestationResponse: CredentialsType;
beforeEach(async () => {
attestationResponse =
await attestationService.createCredentialsType(credentialsTypeDto());
});
it('should call create() from PrismaService.credentialsType', async () => {
expect(PrismaServiceMock().credentialsType.create).toHaveBeenCalled();
});
it('should retrieve created credetialType', async () => {
expect(attestationResponse).toEqual(credentialsTypeDto());
});
});
});
import type CredentialDto from '../../entities/credential.entity.js';
import credDefStub from '../../../credentialDef/tests/stubs/credDef.stub.js';
import AttestationService from '../../services/service.js';
const credentialDto = (): CredentialDto => ({
credentialId: 'credential-id',
credDefId: credDefStub().id,
principalDid: 'principal-did',
threadId: 'thread-id',
state: AttestationService.status.OFFER_SENT,
connectionId: 'connection-id',
});
export default credentialDto;
import type CredentialStateDto from '../../entities/credential.state.entity.js';
import credDefStub from '../../../credentialDef/tests/stubs/credDef.stub.js';
import credentialDto from './credential-dto.js';
const credentialStateDto = (): CredentialStateDto => ({
id: 'credential-state-id',
metadata: {
'_internal/indyCredential': {
credentialDefinitionId: credentialDto().credDefId,
schemaId: credDefStub().schemaID,
},
},
credDefId: credentialDto().credDefId,
state: credentialDto().state,
threadId: 'thread-id',
connectionId: credentialDto().connectionId,
});
export default credentialStateDto;
import type CredentialTypeDto from '../../entities/credentialType.entity.js';
import schemaDto from '../../../schemas/tests/stubs/schema-dto.js';
const credentialsTypeDto = (): CredentialTypeDto => ({
id: 'credential-type-id',
schemaId: schemaDto().schemaID,
type: 'type',
});
export default credentialsTypeDto;
import type OfferCredentialDto from '../../entities/entity.js';
import schemaDto from '../../../schemas/tests/stubs/schema-dto.js';
import credentialDto from './credential-dto.js';
const offerCredentialDto = (): OfferCredentialDto => ({
connectionId: credentialDto().connectionId,
credentialDefinitionId: credentialDto().credDefId,
comment: 'Test Comment!!',
attributes: schemaDto().attributes.map((attr: string) => ({
name: attr,
value: attr,
})),
autoAcceptCredential: 'never',
});
export default offerCredentialDto;
const participantDID = 'participant-did';
export default participantDID;
import type { MicroserviceOptions } from '@nestjs/microservices';
import { VersioningType } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import AppModule from './app.module.js';
import AllExceptionsFilter from './utils/exceptionsFilter.js';
import logger from './utils/logger.js';
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
app.enableCors();
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.NATS,
options: {
servers: [configService.get('nats').url],
},
});
app.enableVersioning({
defaultVersion: ['1'],
type: VersioningType.URI,
});
const swaggerConfig = new DocumentBuilder()
.setTitle('Gaia-x Attestation Manager API')
.setDescription('API documentation for GAIA-X Attestation Manager')
.setVersion('1.0')
.addServer('localhost:3005')
.build();
const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('/swagger', app, document);
await app.startAllMicroservices();
const httpAdapter = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
await app.listen(configService.get('PORT') || 3000, () => {
logger.info(`Listening on Port:${configService.get('PORT')}` || 3000);
});
import type { NestMiddleware } from '@nestjs/common';
import type { NextFunction, Request, Response } from 'express';
import { HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
import logger from '../utils/logger.js';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
public constructor(private readonly configService: ConfigService) {}
/* eslint-disable */
async use(req: Request, res: Response, next: NextFunction) {
if (this.configService.get('auth.useAuth') === 'false') {
return next();
}
logger.info('Request at middleware');
const authHeader = req.headers.authorization;
const authToken = authHeader && authHeader.split(' ')[1];
if (!authToken) {
logger.error('No access token provided.');
res.json({
status: HttpStatus.UNAUTHORIZED,
message: 'Unauthorized. No Access token provided.',
data: undefined,
});
return;
}
const getKey = (
header: jwt.JwtHeader,
callback: jwt.SigningKeyCallback,
): void => {
const jwksUri = this.configService.get('auth.tokenUrl') || '';
const client = jwksClient({ jwksUri, timeout: 30000 });
client
.getSigningKey(header.kid)
.then((key) => callback(null, key.getPublicKey()))
.catch(callback);
};
function verify(token: string): Promise<any> | undefined {
return new Promise(
(resolve: (decoded: any) => void, reject: (error: Error) => void) => {
const verifyCallback: jwt.VerifyCallback<jwt.JwtPayload | string> = (
error: jwt.VerifyErrors | null,
decoded: any,
): void => {
if (error) {
return reject(error);
}
return resolve(decoded);
};
jwt.verify(token, getKey, verifyCallback);
},
);
}
const result = await verify(authToken);
if (!result) {
logger.error('Invalid access token provided.');
res.json({
status: HttpStatus.UNAUTHORIZED,
message: 'Unauthorized. Invalid Access token provided.',
data: undefined,
});
return;
}
next();
}
/* eslint-enable */
}
export default {
AuthMiddleware,
};
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import PrismaService from './prisma.service.js';
@Module({
imports: [ConfigModule],
controllers: [],
providers: [PrismaService],
exports: [PrismaService],
})
export default class PrismaModule {}
import type { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaClient } from '@prisma/client';
@Injectable()
export default class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
public constructor(private configService: ConfigService) {
super();
}
public async onModuleInit() {
const prisma = new PrismaClient({
datasources: {
db: {
url: this.configService.get('DATABASE_URL'),
},
},
});
await prisma.$connect();
}
public async onModuleDestroy() {
await this.$disconnect();
}
}
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Schema {
id String @id @default(uuid())
schemaID String @unique @map("schema_id")
name String
createdBy String @map("created_by")
createdDate DateTime @default(now()) @map("created_date")
updatedBy String @default("") @map("updated_by")
updatedDate DateTime @default(now()) @map("updated_date")
attribute Attributes[]
credential_defs CredentialDef[]
}
model Attributes {
id String @id @default(uuid())
schemaID String @map("schema_id")
name String
createdBy String @map("created_by")
createdDate DateTime @default(now()) @map("created_date")
updatedBy String @default("") @map("updated_by")
updatedDate DateTime @default(now()) @map("updated_date")
schema Schema[]
}
model CredentialDef {
id String @id @default(uuid())
schemaID String @map("schema_id")
name String
credDefId String @unique @map("cred_def_id")
isAutoIssue Boolean @map("is_auto_issue")
isRevokable Boolean @map("is_revokable")
expiryHours String @map("expiry_hours")
createdBy String @map("created_by")
createdDate DateTime @default(now()) @map("created_date")
updatedBy String @default("") @map("updated_by")
updatedDate DateTime @default(now()) @map("updated_date")
schema Schema[]
credentials Credential[]
}
model UserInfo {
id Int @id @default(autoincrement())
connectionId String @map("connection_id")
credentialDefinitionId String? @map("credential_definition_id")
autoAcceptCredential String @map("auto_accept_credential")
status String?
userInfo Json? @map("user_info")
}
model proposeCredential {
id Int @id @default(autoincrement())
connectionId String @map("connection_id")
comment String
credentialProposal String[] @map("credential_proposal")
schemaIssuerDid String @map("schema_issuer_id")
schemaId String @map("schema_id")
schemaName String @map("schema_name")
schemaVersion String @map("schema_version")
credentialDefinitionId String @map("credential_definition_id")
issuerDid String @map("issuer_did")
attachments String
linkedAttachments String @map("linked_attachments")
autoAcceptCredential String @map("auto_accept_credential")
}
model Credential {
id String @id @default(uuid())
credentialId String @unique @map("credential_id")
credDefId String @map("cred_def_id")
threadId String @default(uuid()) @unique @map("thread_id")
state String
principalDid String @map("principal_did")
connectionId String @map("connection_id")
createdDate DateTime @default(now()) @map("created_date")
updatedDate DateTime @default(now()) @map("updated_date")
expirationDate DateTime? @map("expiration_date")
credentialDef CredentialDef[]
}
model CredentialsType {
id String @id @default(uuid())
type String @unique
schemaId String @unique @map("schema_id")
}
import credDefStub from '../../../credentialDef/tests/stubs/credDef.stub.js';
import credentialDto from '../../../issue-credential/tests/stubs/credential-dto.js';
import credentialTypeDto from '../../../issue-credential/tests/stubs/credential-type-dto.js';
import schemaDto from '../../../schemas/tests/stubs/schema-dto.js';
const PrismaServiceMock = jest.fn().mockReturnValue({
$transaction: jest.fn().mockImplementation((args: unknown[]) => [...args]),
schema: {
create: jest.fn().mockReturnValue([schemaDto()]),
update: jest.fn().mockReturnValue([schemaDto()]),
count: jest.fn().mockReturnValue(1),
findMany: jest.fn().mockReturnValue([schemaDto()]),
},
credentialDef: {
create: jest.fn().mockReturnValue([credDefStub()]),
update: jest.fn().mockReturnValue([credDefStub()]),
count: jest.fn().mockReturnValue(1),
findMany: jest.fn().mockReturnValue([credDefStub()]),
findFirst: jest.fn().mockReturnValue(credDefStub()),
},
credential: {
create: jest.fn().mockReturnValue(credentialDto()),
update: jest.fn().mockReturnValue(credentialDto()),
findUnique: jest.fn().mockReturnValue(credentialDto()),
count: jest.fn().mockReturnValue(1),
findMany: jest.fn().mockReturnValue([credentialDto()]),
},
credentialsType: {
update: jest.fn().mockReturnValue(credentialTypeDto()),
create: jest.fn().mockReturnValue(credentialTypeDto()),
findUnique: jest.fn().mockReturnValue(credentialTypeDto()),
findFirst: jest.fn().mockReturnValue(credentialTypeDto()),
},
});
export default PrismaServiceMock;
import type { TestingModule } from '@nestjs/testing';
import { ConfigModule } from '@nestjs/config';
import { Test } from '@nestjs/testing';
import PrismaModule from '../prisma.module.js';
import PrismaService from '../prisma.service.js';
import PrismaServiceMock from './__mocks__/prisma.service.js';
describe('PrismaModule', () => {
let prismaModule: PrismaModule;
const PrismaServiceProvider = {
provide: PrismaService,
useFactory: PrismaServiceMock,
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [ConfigModule],
providers: [PrismaModule, PrismaServiceProvider],
exports: [PrismaService],
}).compile();
prismaModule = module.get<PrismaService>(PrismaService);
});
it('should be defined', () => {
expect(prismaModule).toBeDefined();
});
});
import type { TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { Test } from '@nestjs/testing';
import PrismaService from '../prisma.service.js';
describe('PrismaService', () => {
let prismaService: PrismaService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PrismaService, ConfigService],
}).compile();
prismaService = module.get<PrismaService>(PrismaService);
});
it('should be defined', () => {
expect(prismaService).toBeDefined();
});
describe('onModuleInit()', () => {
let res: unknown;
beforeEach(async () => {
res = await prismaService.onModuleInit();
});
it('should not return anything', async () => {
expect(res).toBeUndefined();
});
});
describe('onModuleDestroy()', () => {
let res: unknown;
beforeEach(async () => {
res = await prismaService.onModuleDestroy();
});
it('should not return anything', async () => {
expect(res).toBeUndefined();
});
});
});