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 1583 deletions
import type { ResponseType } from '../../common/response.js';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type { Response } from 'express';
import {
Body,
Controller,
Get,
HttpStatus,
InternalServerErrorException,
Param,
Post,
Query,
Res,
Version,
} from '@nestjs/common';
import {
ApiBody,
ApiOperation,
ApiParam,
ApiQuery,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { Prisma } from '@prisma/client';
import { VersionRegex } from '../../common/constants.js';
import logger from '../../utils/logger.js';
import SchemaDto from '../entities/schema-entity.js';
import SchemasService from '../services/service.js';
@ApiTags('Schemas')
@Controller('schemas')
export default class SchemasController {
public constructor(private readonly schemasService: SchemasService) {}
@Version(['1'])
@ApiQuery({ name: 'page', required: false })
@ApiQuery({ name: 'pageSize', required: false })
@Get('')
@ApiOperation({
summary: 'Fetch a list of schemas',
description:
'This call provides capabilities to search schemas (which have been created by this OCM) by using pagination. This call returns a list of schemas and overall count of records. Every record contains schemaId, name, attributes',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Schemas fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Schemas fetched successfully': {
value: {
statusCode: 200,
message: 'Schemas fetched successfully',
data: {
count: 2,
records: [
{
id: '1234abcd',
schemaID: 'loremipsum:test-01-01:1.0',
name: 'test-01-01',
createdBy: 'agentName',
createdDate: '1970-01-01T00:00:28.343Z',
updatedBy: '',
updatedDate: '1970-01-01T00:00:28.343Z',
attribute: [
{
name: 'attribute1',
},
{
name: 'attribute2',
},
{
name: 'attributeN',
},
],
},
{
id: '5678abcd',
schemaID: 'loremipsum2:test2-01-01:1.0',
name: 'test2-01-01',
createdBy: 'agentName',
createdDate: '1970-01-01T00:00:28.343Z',
updatedBy: '',
updatedDate: '1970-01-01T00:00:28.343Z',
attribute: [
{
name: 'attribute1',
},
{
name: 'attribute2',
},
{
name: 'attributeN',
},
],
},
],
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'No Data found.',
content: {
'application/json': {
schema: {},
examples: {
'No Data found.': {
value: {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal Server Error.',
content: {
'application/json': {
schema: {},
examples: {
'Internal Server Error.': {
value: {
statusCode: 500,
timestamp: '1970-01-01T00:00:27.897Z',
message: 'Lorem Ipsum',
},
},
},
},
},
})
public async findSchemas(
@Query() query: { pageSize: string; page: string },
@Res() response: Response,
) {
let res: ResponseType;
try {
logger.info('Schemas fetch successfully');
const result = await this.schemasService.findSchemas(
query.pageSize ? parseInt(query.pageSize, 10) : 10,
query.page ? parseInt(query.page, 10) : 0,
);
if (result) {
res = {
statusCode: HttpStatus.OK,
message: 'Schemas fetched successfully',
data: {
count: result[0],
records: result[1],
},
};
} else {
response.status(HttpStatus.NOT_FOUND);
res = {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
};
}
return response.send(res);
} catch (error: unknown) {
throw new InternalServerErrorException(
`Internal Server Error: ${Reflect.get(error || {}, 'message')}`,
);
}
}
@ApiParam({ name: 'id', type: 'string', description: 'Pass schema id' })
@Version(['1'])
@Get('/:id')
@ApiOperation({
summary: 'Fetch schema by id',
description:
'This call provides the capability to get schema data by providing schemaId. The schema data is the same which is returned from /v1/schemas endpoint and contains generic information about schema like schemaID, name, createdBy, createdDate, updatedBy, updatedDate, attribute',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Schema fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Schema fetched successfully': {
value: {
statusCode: 200,
message: 'Schema fetched successfully',
data: {
count: 1,
records: [
{
id: '1234abcd',
schemaID: 'loremipsum:test-01-01:1.0',
name: 'test-01-01',
createdBy: 'agentName',
createdDate: '1970-01-01T00:00:28.343Z',
updatedBy: '',
updatedDate: '1970-01-01T00:00:28.343Z',
attribute: [
{
name: 'attribute1',
},
{
name: 'attribute2',
},
{
name: 'attributeN',
},
],
},
],
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'No Data found.',
content: {
'application/json': {
schema: {},
examples: {
'No Data found.': {
value: {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal Server Error.',
content: {
'application/json': {
schema: {},
examples: {
'Internal Server Error.': {
value: {
statusCode: 500,
timestamp: '1970-01-01T00:00:27.897Z',
message: 'Lorem Ipsum',
},
},
},
},
},
})
public async findSchemasById(
@Param('id') id: string,
@Res() response: Response,
) {
let res: ResponseType;
try {
logger.info('Schema fetched successfully');
const result = await this.schemasService.findSchemasById(id);
if (Array.isArray(result) && result[0] > 0) {
res = {
statusCode: HttpStatus.OK,
message: 'Schema fetch successfully',
data: {
count: result[0],
records: result[1],
},
};
} else {
response.status(HttpStatus.NOT_FOUND);
res = {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
};
}
return response.send(res);
} catch (error) {
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
throw new InternalServerErrorException(error.message);
} else {
throw new InternalServerErrorException(error);
}
}
}
@Version(['1'])
@ApiBody({ type: SchemaDto })
@Post('')
@ApiOperation({
summary: 'Create a new schema',
description:
'This call provides the capability to create new schema on ledger by name, author, version, schema attributes and type. Later this schema can be used to issue new credential definition. This call returns an information about created schema.',
})
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Schema created successfully',
content: {
'application/json': {
schema: {},
examples: {
'Schema created successfully': {
value: {
statusCode: 201,
message: 'Schema created successfully',
data: {
id: '1234qwer',
schemaID: 'loremipsum:2:test-02-01:1.0',
name: 'test-02-01',
createdBy: 'agentName',
createdDate: '1970-01-01T00:00:28.343Z',
updatedBy: '',
updatedDate: '1970-01-01T00:00:28.343Z',
attribute: [
{
name: 'attribute1',
},
{
name: 'attribute2',
},
{
name: 'attributeN',
},
{
name: 'attributeN+1',
},
],
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description:
'Schema required following attributes ( name, createdBy, version, attributes )',
content: {
'application/json': {
schema: {},
examples: {
'Schema required following attributes ( name, createdBy, version, attributes )':
{
value: {
statusCode: 400,
message:
'Schema required following attributes ( name, createdBy, version, attributes )',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Could not get agent details. please try again.',
content: {
'application/json': {
schema: {},
examples: {
'Could not get agent details. please try again.': {
value: {
statusCode: 400,
message: 'Could not get agent details. please try again.',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.CONFLICT,
description: 'Schema already exists',
content: {
'application/json': {
schema: {},
examples: {
'Schema already exists': {
value: {
statusCode: 409,
message: 'Schema already exists',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal Server Error.',
content: {
'application/json': {
schema: {},
examples: {
'Internal Server Error.': {
value: {
statusCode: 500,
timestamp: '1970-01-01T00:00:27.897Z',
message: 'Lorem Ipsum',
},
},
},
},
},
})
public async createSchema(
@Body() createSchema: SchemaDto,
@Res() response: Response,
) {
try {
let res: ResponseType;
if (
!(
createSchema.name &&
typeof createSchema.name === 'string' &&
createSchema.createdBy &&
typeof createSchema.createdBy === 'string' &&
createSchema.version &&
typeof createSchema.version === 'string' &&
VersionRegex.test(createSchema.version) &&
Array.isArray(createSchema.attributes) &&
createSchema.attributes.length > 0 &&
createSchema.attributes.every(
(i) => typeof i === 'string' && i.trim().length > 0,
)
)
) {
res = {
statusCode: HttpStatus.BAD_REQUEST,
message:
'Schema required following attributes ( name, createdBy, version, attributes )',
};
return response.status(HttpStatus.BAD_REQUEST).send(res);
}
createSchema.attributes.push('expirationDate');
const schemaResponse =
await this.schemasService.checkSchemasByNameAndVersion(createSchema);
if (schemaResponse[0] === 0) {
const resp =
await this.schemasService.createSchemaOnLedger(createSchema);
if (resp?.id) {
const schemaRes: SchemaDto = createSchema;
schemaRes.schemaID = resp.id;
response.status(HttpStatus.CREATED);
res = {
statusCode: HttpStatus.CREATED,
message: 'Schema created successfully',
data: await this.schemasService.createSchemas(schemaRes),
};
logger.info('Schema created successfully');
} else {
response.status(HttpStatus.BAD_REQUEST);
res = {
statusCode: HttpStatus.BAD_REQUEST,
message: 'Could not get agent details. please try again.',
};
}
} else {
response.status(HttpStatus.CONFLICT);
res = {
statusCode: HttpStatus.CONFLICT,
message: 'Schema already exists',
};
}
return response.send(res);
} catch (error) {
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
throw new InternalServerErrorException(error.message);
} else {
throw new InternalServerErrorException(error);
}
}
}
@Version(['1'])
@Get('/get-dids-for-schema/:id')
@ApiOperation({
summary: 'Fetch list of dids for schema id',
description:
'This call provides the capability to get principal dids. The format of the response is shown in the example. To issue credentials, you need to have a credential definition. This is a basic principle of this process. This credential definition is created by using the schema. Using this endpoint gives you all dids of participants to whom OCM issued credentials using specified schema.',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Schema DIDs fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Schema DIDs fetched successfully': {
value: {
statusCode: 200,
message: 'Schema DIDs fetched successfully',
data: {
total_credential_defs: 1,
principalDids: [
{
loremIpsumCredDef: ['1234did'],
},
],
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'No Data found.',
content: {
'application/json': {
schema: {},
examples: {
'No Data found.': {
value: {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal Server Error.',
content: {
'application/json': {
schema: {},
examples: {
'Internal Server Error.': {
value: {
statusCode: 500,
timestamp: '1970-01-01T00:00:27.897Z',
message: 'Lorem Ipsum',
},
},
},
},
},
})
public async getDidsForSchema(
@Param('id') id: string,
@Res() response: Response,
) {
let res: ResponseType;
try {
logger.info('Schema DIDs fetched successfully');
const result = await this.schemasService.getDidsForSchemasId(id);
if (result && result.length) {
const schema = result[0];
const credDefs = schema.credential_defs;
const principalDids = credDefs.map((cd) => ({
[cd.credDefId]: cd.credentials.map((cred) => cred.principalDid),
}));
const data = {
total_credential_defs: credDefs.length,
principalDids,
};
res = {
statusCode: HttpStatus.OK,
message: 'Schema DIDs fetched successfully',
data,
};
} else {
response.status(HttpStatus.NOT_FOUND);
res = {
statusCode: HttpStatus.NOT_FOUND,
message: 'No Data found',
};
}
return response.send(res);
} catch (error) {
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
throw new InternalServerErrorException(error.message);
} else {
throw new InternalServerErrorException(error);
}
}
}
}
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsString, IsNotEmpty } from 'class-validator';
export default class SchemaDto {
@IsString()
@IsNotEmpty()
public id: string;
@IsString()
@IsNotEmpty()
public schemaID: string;
@IsString()
@IsNotEmpty()
@ApiProperty()
public name: string;
@IsString()
@ApiProperty()
public createdBy: string;
@IsString()
public createdDate: Date;
@IsString()
public updatedBy?: string;
@IsString()
public updatedDate: Date;
@IsString()
@ApiProperty()
public version: string;
@IsString()
@ApiProperty()
public attributes: string[];
@IsString()
public pageSize?: string;
@IsString()
public page?: string;
@IsString()
@ApiPropertyOptional()
public type?: string;
}
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import NatsClientService from '../client/nats.client.js';
import RestClientService from '../client/rest.client.js';
import { NATSServices } from '../common/constants.js';
import config from '../config/config.js';
import PrismaService from '../prisma/prisma.service.js';
import SchemasController from './controller/controller.js';
import SchemasService from './services/service.js';
@Module({
imports: [
HttpModule,
ClientsModule.register([
{
name: NATSServices.SERVICE_NAME,
transport: Transport.NATS,
options: {
servers: [config().nats.url as string],
},
},
]),
],
controllers: [SchemasController],
providers: [
SchemasService,
PrismaService,
NatsClientService,
RestClientService,
],
})
export default class SchemasModule {}
import type { Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
@Injectable()
export default class SchemaRepository {
public constructor(private readonly prismaService: PrismaService) {}
public async createSchema(data: Prisma.SchemaCreateInput) {
return this.prismaService.schema.create({
data,
include: {
attribute: {
select: {
name: true,
},
},
},
});
}
public async findSchemas(params: {
skip?: number;
take?: number;
cursor?: Prisma.SchemaWhereUniqueInput;
where?: Prisma.SchemaWhereInput;
orderBy?: Prisma.SchemaOrderByWithRelationInput;
}) {
const { skip, take, cursor, where, orderBy } = params;
return this.prismaService.$transaction([
this.prismaService.schema.count({
where,
}),
this.prismaService.schema.findMany({
skip,
take,
cursor,
where,
orderBy,
include: {
attribute: {
select: {
name: true,
},
},
},
}),
]);
}
public async findUniqueSchema(params: {
where: Prisma.SchemaWhereUniqueInput;
}) {
const { where } = params;
return this.prismaService.schema.findUnique({
where,
include: {
attribute: {
select: {
name: true,
},
},
},
});
}
}
import type SchemaDto from '../entities/schema-entity.js';
import type { Prisma } from '@prisma/client';
import { BadRequestException, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import CredentialTypeRepository from '../../issue-credential/repository/credentialType.repository.js';
import PrismaService from '../../prisma/prisma.service.js';
import pagination from '../../utils/pagination.js';
import SchemaRepository from '../repository/schema.respository.js';
@Injectable()
export default class SchemasService {
private schemaRepository: SchemaRepository;
private credentialTypeRepository: CredentialTypeRepository;
public constructor(
private readonly prismaService: PrismaService,
private readonly restClient: RestClientService,
private readonly natsClient: NatsClientService,
private readonly configService: ConfigService,
) {
this.schemaRepository = new SchemaRepository(this.prismaService);
this.credentialTypeRepository = new CredentialTypeRepository(
this.prismaService,
);
}
public async createSchemas(schema: SchemaDto) {
const query: {
schemaID: string;
name: string;
createdBy: string;
attribute: {
create: {
schemaID: string;
name: string;
createdBy: string;
}[];
};
} = {
schemaID: schema.schemaID,
name: schema.name,
createdBy: schema.createdBy,
attribute: {
create: [],
},
};
schema.attributes.forEach((element) => {
query.attribute.create.push({
schemaID: schema.schemaID,
name: element,
createdBy: schema.createdBy,
});
});
if (
schema.type &&
typeof schema.type === 'string' &&
schema.type.trim().length > 0
) {
await this.credentialTypeRepository.createOrUpdateCredentialsType({
schemaId: query.schemaID,
type: schema.type.trim(),
});
}
return this.schemaRepository.createSchema(query);
}
public async findSchemas(pageSize: number, page: number) {
let query: {
skip?: number;
take?: number;
cursor?: Prisma.SchemaWhereUniqueInput;
where?: Prisma.SchemaWhereInput;
orderBy?: Prisma.SchemaOrderByWithRelationInput;
} = {};
query = { ...query, ...pagination(pageSize, page) };
return this.schemaRepository.findSchemas(query);
}
public async findSchemasById(id: string) {
return this.schemaRepository.findSchemas({
where: { schemaID: id },
});
}
public async getDidsForSchemasId(id: string) {
return this.prismaService.schema.findMany({
where: { schemaID: id },
include: {
attribute: true,
credential_defs: {
include: {
credentials: {
distinct: 'principalDid',
},
},
},
},
});
}
public findBySchemaId(schemaID: string) {
const query = { where: { schemaID } };
return this.schemaRepository.findUniqueSchema(query);
}
public async checkSchemasByNameAndVersion(schemaDto: SchemaDto) {
return this.schemaRepository.findSchemas({
where: {
schemaID: {
endsWith: `:${schemaDto.version}`,
mode: 'insensitive', // Default value: default
},
name: {
equals: schemaDto.name, // Default mode
},
},
});
}
public async createSchemaOnLedger(schemaDto: SchemaDto) {
const agentUrl = this.configService.get('agent.AGENT_URL');
const responseData = await this.restClient.post(
`${agentUrl}/schemas/`,
schemaDto,
);
return responseData;
}
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;
}
}
import schemaDto from '../stubs/schema-dto.js';
const SchemasServiceMock = jest.fn().mockReturnValue({
findSchemas: jest.fn().mockReturnValue([1, [schemaDto()]]),
findSchemasById: jest.fn().mockReturnValue([1, [schemaDto()]]),
findBySchemaId: jest.fn().mockReturnValue(schemaDto()),
checkSchemasByNameAndVersion: jest.fn().mockReturnValue([0, []]),
createSchemaOnLedger: jest.fn().mockReturnValue({ id: schemaDto().schemaID }),
createSchemas: jest.fn().mockReturnValue(schemaDto()),
});
export default SchemasServiceMock;
import type SchemaDto from '../entities/schema-entity.js';
import type { TestingModule } from '@nestjs/testing';
import type { Response } from 'express';
import { HttpStatus } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { createResponse } from 'node-mocks-http';
import SchemasController from '../controller/controller.js';
import SchemasService from '../services/service.js';
import SchemasServiceMock from './__mocks__/service.js';
import schemaDto from './stubs/schema-dto.js';
describe('SchemasController', () => {
let schemasController: SchemasController;
let schemasService: SchemasService;
beforeEach(async () => {
const SchemasServiceProvider = {
provide: SchemasService,
useFactory: SchemasServiceMock,
};
const module: TestingModule = await Test.createTestingModule({
controllers: [SchemasController],
providers: [SchemasServiceProvider],
}).compile();
schemasController = module.get<SchemasController>(SchemasController);
schemasService = module.get<SchemasService>(SchemasService);
jest.clearAllMocks();
});
it('should be defined', () => {
expect(schemasController).toBeDefined();
});
describe('findSchemas()', () => {
let schemasResponse: Response<string, Record<string, unknown>>;
let query: SchemaDto;
let response: Response<string, Record<string, unknown>>;
beforeEach(async () => {
query = schemaDto();
response = createResponse();
schemasResponse = await schemasController.findSchemas(query, response);
});
it('should call findSchemas() from service', async () => {
expect(schemasService.findSchemas).toHaveBeenCalled();
});
it('should retrieve schemas by query', async () => {
expect(
schemasService.findSchemas(
query.pageSize ? parseInt(query.pageSize, 10) : 10,
query.page ? parseInt(query.page, 10) : 0,
),
).toEqual([1, [schemaDto()]]);
});
it(`should retrieve HTTP status OK(${HttpStatus.OK})`, async () => {
expect(schemasResponse?.statusCode).toEqual(HttpStatus.OK);
});
});
describe('findSchemasById()', () => {
let schemasResponse: Response<string, Record<string, unknown>>;
let id: string;
let response: Response<string, Record<string, unknown>>;
beforeEach(async () => {
id = schemaDto().schemaID || '';
response = createResponse();
schemasResponse = await schemasController.findSchemasById(id, response);
});
it('should call findSchemasById() from service', async () => {
expect(schemasService.findSchemasById).toHaveBeenCalled();
});
it('should retrieve schema by ID', async () => {
expect(schemasService.findSchemasById(id)).toEqual([1, [schemaDto()]]);
});
it(`should retrieve HTTP status OK(${HttpStatus.OK})`, async () => {
expect(schemasResponse?.statusCode).toEqual(HttpStatus.OK);
});
});
describe('createSchema()', () => {
let schemasResponse: Response<string, Record<string, unknown>>;
let createSchema: SchemaDto;
let response: Response<string, Record<string, unknown>>;
beforeEach(async () => {
createSchema = schemaDto();
response = createResponse();
schemasResponse = await schemasController.createSchema(
createSchema,
response,
);
});
it('should call checkSchemasByNameAndVersion() from service', async () => {
expect(schemasService.checkSchemasByNameAndVersion).toHaveBeenCalled();
});
it('should not retrieve any existing schema', async () => {
expect(
schemasService.checkSchemasByNameAndVersion(createSchema),
).toContain(0);
});
it('should call createSchemaOnLedger() from service', async () => {
expect(schemasService.createSchemaOnLedger).toHaveBeenCalled();
});
it('should retrieve schema with ID', async () => {
expect(schemasService.createSchemaOnLedger(createSchema)).toEqual({
id: schemaDto().schemaID,
});
});
it('should call createSchemas() from service', async () => {
expect(schemasService.createSchemas).toHaveBeenCalled();
});
it('should retrieve created schema', async () => {
expect(schemasService.createSchemas(createSchema)).toEqual(schemaDto());
});
it(`should retrieve HTTP status created(${HttpStatus.CREATED})`, async () => {
expect(schemasResponse?.statusCode).toEqual(HttpStatus.CREATED);
});
});
});
import type { TestingModule } from '@nestjs/testing';
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 PrismaService from '../../prisma/prisma.service.js';
import PrismaServiceMock from '../../prisma/tests/__mocks__/prisma.service.js';
import SchemasModule from '../module.js';
import SchemasService from '../services/service.js';
import SchemasServiceMock from './__mocks__/service.js';
describe('SchemasModule', () => {
let schemasModule: SchemasModule;
const SchemasServiceProvider = {
provide: SchemasService,
useFactory: SchemasServiceMock,
};
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: [
SchemasModule,
SchemasServiceProvider,
PrismaServiceProvider,
NatsClientServiceProvider,
RestClientServiceProvider,
ConfigService,
],
}).compile();
schemasModule = module.get<SchemasModule>(SchemasModule);
});
it('should be defined', () => {
expect(schemasModule).toBeDefined();
});
});
import type { ResponseType } from '../../common/response.js';
import type { TestingModule } from '@nestjs/testing';
import type { Schema } from 'joi';
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 PrismaService from '../../prisma/prisma.service.js';
import PrismaServiceMock from '../../prisma/tests/__mocks__/prisma.service.js';
import SchemasService from '../services/service.js';
import schemaDto from './stubs/schema-dto.js';
describe('SchemasService', () => {
let schemasService: SchemasService;
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: [
SchemasService,
PrismaServiceProvider,
RestClientServiceProvider,
NatsClientServiceProvider,
ConfigService,
],
}).compile();
schemasService = module.get<SchemasService>(SchemasService);
});
it('should be defined', () => {
expect(schemasService).toBeDefined();
});
describe('createSchemas()', () => {
let schemasResponse: Schema;
beforeEach(async () => {
schemasResponse = await schemasService.createSchemas(schemaDto());
});
it('should call create() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.create).toHaveBeenCalled();
});
it('should retrieve created schema', async () => {
expect(schemasResponse).toEqual([schemaDto()]);
});
});
describe('findSchemas()', () => {
let schemasResponse: Array<number | Schema[]>;
beforeEach(async () => {
const pageSize = parseInt(schemaDto().pageSize || '', 10);
const page = parseInt(schemaDto().page || '', 10);
schemasResponse = await schemasService.findSchemas(pageSize, page);
});
it('should call findMany() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.findMany).toHaveBeenCalled();
});
it('should call count() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.count).toHaveBeenCalled();
});
it('should call $transaction() from PrismaService', async () => {
expect(PrismaServiceMock().$transaction).toHaveBeenCalled();
});
it('should retrieve schemas by participantId', async () => {
expect(schemasResponse).toEqual([1, [schemaDto()]]);
});
});
describe('findSchemasById()', () => {
let schemasResponse: Array<number | Schema[]>;
let id: string;
beforeEach(async () => {
id = schemaDto().schemaID || '';
schemasResponse = await schemasService.findSchemasById(id);
});
it('should call findMany() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.findMany).toHaveBeenCalled();
});
it('should call count() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.count).toHaveBeenCalled();
});
it('should call $transaction() from PrismaService', async () => {
expect(PrismaServiceMock().$transaction).toHaveBeenCalled();
});
it('should retrieve schema by Schema ID', async () => {
expect(schemasResponse).toEqual([1, [schemaDto()]]);
});
});
describe('checkSchemasByNameAndVersion()', () => {
let schemasResponse: Array<number | Schema[]>;
beforeEach(async () => {
schemasResponse =
await schemasService.checkSchemasByNameAndVersion(schemaDto());
});
it('should call findMany() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.findMany).toHaveBeenCalled();
});
it('should call count() from PrismaService.schema', async () => {
expect(PrismaServiceMock().schema.count).toHaveBeenCalled();
});
it('should call $transaction() from PrismaService', async () => {
expect(PrismaServiceMock().$transaction).toHaveBeenCalled();
});
it('should retrieve schemas by Name and Version', async () => {
expect(schemasResponse).toEqual([1, [schemaDto()]]);
});
});
describe('createSchemaOnLedger()', () => {
let schemasResponse: ResponseType;
beforeEach(async () => {
schemasResponse = await schemasService.createSchemaOnLedger(schemaDto());
});
it('should call post() from restClient', async () => {
expect(RestClientServiceMock().post).toHaveBeenCalled();
});
it('should get a response from AFJ', async () => {
expect(schemasResponse).not.toBe(null);
});
});
});
import type SchemaDto from '../../entities/schema-entity.js';
const schemaDto = (): SchemaDto => ({
id: 'schema-db-id',
schemaID: 'schema-ledger-id',
name: 'schema-name',
createdBy: 'created-by',
createdDate: new Date(2022),
updatedBy: 'updated-by',
updatedDate: new Date(2022),
version: '0.0.1',
attributes: ['attr1', 'attr2', 'attr3'],
pageSize: '3',
page: '3',
type: 'testing',
});
export default schemaDto;
import schemaDto from './schema-dto.js';
const schemaAgentDto = {
ver: '1.0',
id: schemaDto().schemaID,
name: schemaDto().name,
version: schemaDto().version,
attrNames: [...schemaDto().attributes, 'expirationDate'],
seqNo: 335519,
};
export default schemaAgentDto;
import { Observable } from 'rxjs';
const HttpServiceMock = jest.fn().mockReturnValue({
post: jest.fn().mockReturnValue(
new Observable((subscriber) => {
subscriber.next({
data: {
sample: 'data',
},
});
subscriber.complete();
}),
),
});
export default HttpServiceMock;
import type { INestApplication } from '@nestjs/common';
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import AppModule from '../app.module.js';
describe('App Module', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('should work', () => {
expect(true).toBe(true);
});
});
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type { Response } from 'express';
import {
BadRequestException,
Body,
Controller,
HttpException,
HttpStatus,
InternalServerErrorException,
Post,
Res,
Version,
} from '@nestjs/common';
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Prisma } from '@prisma/client';
import { isUUID } from 'class-validator';
import { AutoAcceptCredential } from '../../common/constants.js';
import logger from '../../utils/logger.js';
import UserInfoDto from '../entities/userInfo.entity.js';
import UserInfoService from '../services/service.js';
@ApiTags('userInfo (to be deprecated)')
@Controller('userInfo')
export default class UserInfoController {
public constructor(private readonly userInfoService: UserInfoService) {}
@Version(['1'])
@ApiBody({ type: UserInfoDto })
@Post('')
@ApiOperation({
summary: 'Add user information to a connection',
description:
'This call provides the capability to add any additional information to connection. The format of added data is just a simple json',
})
public async createUserInfo(
@Body() userInfoDto: UserInfoDto,
@Res() response: Response,
) {
try {
logger.info(`UserInfoDto: ${JSON.stringify(UserInfoDto)}`);
const { autoAcceptCredential, connectionId, userInfo } = userInfoDto;
if (!connectionId || !isUUID(connectionId)) {
throw new BadRequestException('Invalid connection ID');
}
if (
autoAcceptCredential &&
autoAcceptCredential in AutoAcceptCredential
) {
throw new BadRequestException('Invalid autoAcceptCredential');
}
if (!userInfo || Object.values(userInfo).length === 0) {
throw new BadRequestException('Invalid userInfo');
}
const res = {
statusCode: HttpStatus.CREATED,
message: 'User info created successfully',
data: await this.userInfoService.createUserInfo(userInfoDto),
};
return response.send(res);
} catch (error: unknown) {
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
throw new InternalServerErrorException(error.message);
} else {
throw new HttpException(
Reflect.get(error || {}, 'message') || 'Internal server error',
Reflect.get(error || {}, 'status') || 500,
);
}
}
}
}
type UserInfo = {
[key: string]: unknown;
};
export default class UpdateUserInfoDto {
public connectionId: string;
public status: string;
public credentialDefinitionId: string;
public userInfo: UserInfo;
}
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
type UserInfo = {
[key: string]: unknown;
};
export default class UserInfoDto {
@IsString()
@IsNotEmpty()
@ApiProperty()
public connectionId: string;
@IsEnum(['always', 'contentApproved', 'never'])
@IsNotEmpty()
@ApiProperty()
public autoAcceptCredential: string;
@IsNotEmpty()
@ApiProperty({ type: {} })
public userInfo: UserInfo;
}
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import NatsClientService from '../client/nats.client.js';
import RestClientService from '../client/rest.client.js';
import { NATSServices } from '../common/constants.js';
import config from '../config/config.js';
import PrismaService from '../prisma/prisma.service.js';
import SchemasService from '../schemas/services/service.js';
import UserInfoController from './controller/controller.js';
import UserInfoService from './services/service.js';
@Module({
imports: [
HttpModule,
ClientsModule.register([
{
name: NATSServices.SERVICE_NAME,
transport: Transport.NATS,
options: {
servers: [config().nats.url as string],
},
},
]),
],
controllers: [UserInfoController],
providers: [
UserInfoService,
PrismaService,
NatsClientService,
RestClientService,
SchemasService,
],
})
export default class UserInfoModule {}
import type { Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
@Injectable()
export default class UserInfoRepository {
public constructor(private readonly prismaService: PrismaService) {}
public async createUserInfo(data: Prisma.UserInfoCreateInput) {
const userInfo = await this.prismaService.userInfo.create({
data,
});
return userInfo;
}
public async updateUserInfo(data: Prisma.UserInfoUncheckedUpdateManyInput) {
const userInfo = await this.prismaService.userInfo.updateMany({
where: {
connectionId: data.connectionId as string,
},
data,
});
return userInfo;
}
public async getUserInfo(params: { where: Prisma.UserInfoWhereInput }) {
const { where } = params;
return this.prismaService.userInfo.findFirst({
where,
});
}
}
import type UpdateUserInfoDto from '../entities/update-unserInfo.dto.js';
import type UserInfoDto from '../entities/userInfo.entity.js';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
import logger from '../../utils/logger.js';
import UserInfoRepository from '../repository/userInfo.respository.js';
@Injectable()
export default class UserInfoService {
private userInfoRepository: UserInfoRepository;
public constructor(private readonly prismaService: PrismaService) {
this.userInfoRepository = new UserInfoRepository(this.prismaService);
}
public async createUserInfo(userInfoDto: UserInfoDto) {
logger.info(`In user info service, ${JSON.stringify(userInfoDto)}`);
return this.userInfoRepository.createUserInfo({
autoAcceptCredential: userInfoDto.autoAcceptCredential,
connectionId: userInfoDto.connectionId,
userInfo: userInfoDto.userInfo as object,
});
}
public async updateUserInfo(userInfoDto: UpdateUserInfoDto) {
logger.info(`In user info service, ${userInfoDto}`);
return this.userInfoRepository.updateUserInfo({
connectionId: userInfoDto.connectionId,
credentialDefinitionId: userInfoDto.credentialDefinitionId,
status: userInfoDto.status,
userInfo: userInfoDto.userInfo as object,
});
}
public async getUserInfo(connectionId: string) {
logger.info(`In get user info service, ${connectionId}`);
return this.userInfoRepository.getUserInfo({
where: {
connectionId,
},
});
}
}
import userInfo from '../stubs/user-info-dto.stub.js';
const UserInfoServiceMock = jest.fn().mockReturnValue({
createUserInfo: jest.fn().mockReturnValue(userInfo),
updateUserInfo: jest.fn().mockReturnValue(userInfo()),
getUserInfo: jest.fn().mockReturnValue(userInfo()),
});
export default UserInfoServiceMock;