Skip to content

Commit

Permalink
feat(vc-api): change the verify records api request + add dto to _ver…
Browse files Browse the repository at this point in the history
…ifyRecords function + unit tests fixes
  • Loading branch information
AngeloAyranji committed Nov 20, 2024
1 parent ae935e3 commit 8ba0d17
Show file tree
Hide file tree
Showing 17 changed files with 114 additions and 62 deletions.
2 changes: 2 additions & 0 deletions apps/vc-api/src/api/filters/vc.api.filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SocialResolverNotFoundExceptionFilter } from './credentials/social-reso
import { CredentialsExceptionFilter } from './credentials/credentials.filter';
import { AuthenticationExceptionFilter } from './auth/authentication.filter';
import { JustaNameInitializerExceptionFilter } from './auth/justaName-intializer.filter';
import { Web3ProviderExceptionFilter } from './web3-provider/web3-provider.filter';

export const VCManagementApiFilters = [
OTPExceptionFilter,
Expand All @@ -14,4 +15,5 @@ export const VCManagementApiFilters = [
ChainIdInvalidExceptionFilter,
CredentialsExceptionFilter,
SocialResolverNotFoundExceptionFilter,
Web3ProviderExceptionFilter,
];
16 changes: 16 additions & 0 deletions apps/vc-api/src/api/filters/web3-provider/web3-provider.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ArgumentsHost, Catch, HttpStatus } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { Web3ProviderException } from '../../../core/domain/exceptions/Web3Provider.exception';

@Catch(Web3ProviderException)
export class Web3ProviderExceptionFilter extends BaseExceptionFilter {
catch(exception: Web3ProviderException, host: ArgumentsHost) {
const context = host.switchToHttp();
const response = context.getResponse();
const httpStatus = HttpStatus.BAD_REQUEST;

response.status(httpStatus).json({
message: exception.message,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export class VerifyRecordsControllerMapper implements IVerifyRecordsControllerMa
verifyRecordsApiRequest: VerifyRecordsApiRequest,
): VerifyRecordsRequest {
return {
providerUrl: verifyRecordsApiRequest.providerUrl,
ens: verifyRecordsApiRequest.ens,
chainId: verifyRecordsApiRequest.chainId,
credentials: verifyRecordsApiRequest.credentials,
issuer: verifyRecordsApiRequest.issuer,
matchStandard: verifyRecordsApiRequest.matchStandard
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { IsArray, IsInt, IsOptional, IsString } from 'class-validator';
import { IsArray, IsOptional, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import { ChainId } from '@justaname.id/sdk';
import { Transform } from 'class-transformer';
import { Credentials } from '../../../core/domain/entities/credentials';

export class VerifyRecordsApiRequest {
@ApiProperty()
@IsString()
providerUrl: string;

@ApiProperty()
@IsArray()
@IsString({ each: true })
@Transform(({ value }) => (Array.isArray(value) ? value : [value]))
ens: string[];

@ApiProperty()
@Type(() => Number)
@IsInt()
chainId: ChainId;

@ApiProperty()
@IsArray()
@IsString({ each: true })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const FETCH_CHAIN_ID_SERVICE = 'FETCH_CHAIN_ID_SERVICE';

export interface IFetchChainIdService {
getChainId(providerUrl: string): Promise<number>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { Subname } from '../../domain/entities/subname';
export const SUBNAME_RECORDS_FETCHER = 'SUBNAME_RECORDS_FETCHER';

export interface ISubnameRecordsFetcher {
fetchRecords(subname: string, chainId: ChainId, texts?: string[]): Promise<Subname>;
fetchRecordsFromManySubnames(subnames: string[], chainId: ChainId, texts?: string[]): Promise<Subname[]>
fetchRecords(providerUrl: string, subname: string, chainId: ChainId, texts?: string[]): Promise<Subname>;
fetchRecordsFromManySubnames(providerUrl: string, subnames: string[], chainId: ChainId, texts?: string[]): Promise<Subname[]>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export class VerifyRecordsRequest {
providerUrl: string;
ens: string[];
issuer: string;
chainId: number;
credentials: string[];
matchStandard?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class RecordVerifierCheckerResponse {
[key: string]: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {TelegramCredential} from "../../domain/credentials/telegram.credential";
import {EmailCredential} from "../../domain/credentials/email.credential";
import {DiscordCredential} from "../../domain/credentials/discord.credential";
import { ChainIdInvalidException } from '../../domain/exceptions/ChainIdInvalid.exception';
import { FETCH_CHAIN_ID_SERVICE, IFetchChainIdService } from '../provider-services/ifetch-chain-id.service';
import { RecordVerifierCheckerResponse } from './response/record-verifier-checker.response';

@Injectable()
export class VerifyRecordsService implements IVerifyRecordsService {
Expand All @@ -25,20 +27,23 @@ export class VerifyRecordsService implements IVerifyRecordsService {
@Inject(SUBNAME_RECORDS_FETCHER)
private readonly subnameRecordsFetcher: ISubnameRecordsFetcher,
@Inject(ENVIRONMENT_GETTER) private readonly environmentGetter: IEnvironmentGetter,
@Inject(FETCH_CHAIN_ID_SERVICE) private readonly fetchChainIdService: IFetchChainIdService,
) {
this.domain = this.environmentGetter.getEnsDomain();
}

async verifyRecords(verifyRecordsRequest: VerifyRecordsRequest): Promise<VerifyRecordsResponse[]> {
const { ens, chainId, credentials, issuer } = verifyRecordsRequest;
const { ens, credentials, issuer, providerUrl } = verifyRecordsRequest;

const validIssuer = issuer ? issuer : this.domain;

const chainId = await this.fetchChainIdService.getChainId(providerUrl);

if (chainId !== 1 && chainId !== 11155111) {
throw ChainIdInvalidException.withId(chainId);
}

const subnameRecords = await this.subnameRecordsFetcher.fetchRecordsFromManySubnames(ens, chainId, [...credentials, ...credentials.map((record) => `${record}_${validIssuer}`)]);
const subnameRecords = await this.subnameRecordsFetcher.fetchRecordsFromManySubnames(providerUrl, ens, chainId, [...credentials, ...credentials.map((record) => `${record}_${validIssuer}`)]);
const responses: VerifyRecordsResponse[] = [];

for (const subnameRecord of subnameRecords) {
Expand All @@ -56,7 +61,7 @@ export class VerifyRecordsService implements IVerifyRecordsService {
return responses;
}

private _recordVerifier(record: string, subnameRecords: Subname, chainId: number, issuer: string, matchStandard: boolean): { [key: string]: boolean } {
private _recordVerifier(record: string, subnameRecords: Subname, chainId: number, issuer: string, matchStandard: boolean): RecordVerifierCheckerResponse {
// 1) check if record_issuer exists in subnameRecords, if not return false
const foundRecordIssuer = subnameRecords.metadata.textRecords.find((item) => item.key === `${record}_${issuer}`);
if (!foundRecordIssuer) {
Expand Down
10 changes: 10 additions & 0 deletions apps/vc-api/src/core/domain/exceptions/Web3Provider.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class Web3ProviderException extends Error {
private constructor(message: string) {
super(message);
}

static withMessage(providerUrl: string): Web3ProviderException {
const message = `Web3ProviderException: Provider Url ${providerUrl} threw an error`;
return new Web3ProviderException(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@nestjs/common';
import { ethers } from 'ethers';
import { IFetchChainIdService } from '../../core/applications/provider-services/ifetch-chain-id.service';
import { Web3ProviderException } from '../../core/domain/exceptions/Web3Provider.exception';

@Injectable()
export class FetchChainIdService implements IFetchChainIdService {
providerChainIdMap: Map<string, number> = new Map();


async getChainId(providerUrl: string): Promise<number> {
try {
if (this.providerChainIdMap.has(providerUrl)) {
return this.providerChainIdMap.get(providerUrl) as number;
}

const provider = new ethers.JsonRpcProvider(providerUrl);
const network = await provider.getNetwork();

this.providerChainIdMap.set(providerUrl, Number(network.chainId));

return Number(network.chainId)
} catch (e) {
throw Web3ProviderException.withMessage(providerUrl);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ export class SubnameRecordsFetcher implements ISubnameRecordsFetcher {
@Inject(ENS_MANAGER_SERVICE) private readonly ensManagerService: IEnsManagerService
) {}

async fetchRecords(subname: string, chainId: ChainId, texts?: string[]): Promise<Subname> {

async fetchRecords(providerUrl: string, subname: string, chainId: ChainId, texts?: string[]): Promise<Subname> {
let records: GetRecordsResponse;
const providerUrl = (chainId === 1 ? 'https://mainnet.infura.io/v3/' :'https://sepolia.infura.io/v3/') + this.environmentGetter.getInfuraProjectId()

if(texts) {
const client = createClient({
Expand Down Expand Up @@ -48,10 +46,8 @@ export class SubnameRecordsFetcher implements ISubnameRecordsFetcher {
return this.mapSubnameRecordsResponseToSubname(subname, records);
}

async fetchRecordsFromManySubnames(subnames: string[], chainId: ChainId, texts?: string[]): Promise<Subname[]> {

async fetchRecordsFromManySubnames(providerUrl: string, subnames: string[], chainId: ChainId, texts?: string[]): Promise<Subname[]> {
let records: GetRecordsResponse[];
const providerUrl = (chainId === 1 ? 'https://mainnet.infura.io/v3/' :'https://sepolia.infura.io/v3/') + this.environmentGetter.getInfuraProjectId()

if(texts) {
const client = createClient({
Expand Down
6 changes: 6 additions & 0 deletions apps/vc-api/src/vc-management.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import {EmailResolver} from "./core/applications/credentials/facade/email-resolv
import {EMAIL_RESOLVER} from "./core/applications/credentials/facade/email-resolver/iemail.resolver";
import { VCManagementApiFilters } from './api/filters/vc.api.filters';
import { APP_FILTER } from '@nestjs/core';
import { FetchChainIdService } from './external/provider-services/fetch-chain-id.service';
import { FETCH_CHAIN_ID_SERVICE } from './core/applications/provider-services/ifetch-chain-id.service';

const dynamicImport = async (packageName: string) =>
new Function(`return import('${packageName}')`)();
Expand All @@ -81,6 +83,10 @@ const dynamicImport = async (packageName: string) =>
provide: APP_FILTER,
useClass: filter,
})),
{
useClass: FetchChainIdService,
provide: FETCH_CHAIN_ID_SERVICE
},
{
useClass: CredentialsControllerMapper,
provide: AUTH_CONTROLLER_MAPPER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ import { VerifyRecordsRequest } from "../../../../../src/core/applications/verif
import { VerifyRecordsResponse } from "../../../../../src/core/applications/verify-records/response/verify-records.response";
import { VerifyRecordsApiResponse } from "../../../../../src/api/verify-records/responses/verify-records.api.response";
const ENS = 'ENS';
const CHAIN_ID = 1;
const CREDENTIALS = 'github';
const ISSUER = 'ISSUER';
const MATCH_STANDARD = true;
const SUBNAME = 'SUBNAME';
const RECORDS = {
'RECORD_1': true,
};
const PROVIDER_URL = 'PROVIDER_URL';

const getVerifyRecordsApiRequest = (): VerifyRecordsApiRequest => {
return {
ens: [ENS],
chainId: CHAIN_ID,
credentials: [CREDENTIALS],
issuer: ISSUER,
matchStandard: MATCH_STANDARD
matchStandard: MATCH_STANDARD,
providerUrl: PROVIDER_URL
};
};

const getVerifyRecordsRequest = (): VerifyRecordsRequest => {
return {
ens: [ENS],
chainId: CHAIN_ID,
credentials: [CREDENTIALS],
issuer: ISSUER,
matchStandard: MATCH_STANDARD
matchStandard: MATCH_STANDARD,
providerUrl: PROVIDER_URL
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,28 @@ import {
IVerifyRecordsService,
VERIFY_RECORDS_SERVICE,
} from '../../../../src/core/applications/verify-records/iverify-records.service';
import { FETCH_CHAIN_ID_SERVICE, IFetchChainIdService } from '../../../../src/core/applications/provider-services/ifetch-chain-id.service';

const ENS = 'ENS';
const ERROR_MESSAGE = 'ERROR_MESSAGE';
const SOCIAL_CREDENTIAL: SocialCredentials = 'github';
const CHAIN_ID = 11155111;
const CHAIN_ID_2 = 31337;
const ISSUER = 'ISSUER';

const PROVIDER_URL = 'PROVIDER_URL';
const RECORD_KEY = 'RECORD_KEY';
const RECORD_VALUE = true;

const getVerifyRecordsApiRequest = (): VerifyRecordsApiRequest => ({
ens: [ENS],
chainId: CHAIN_ID,
credentials: [SOCIAL_CREDENTIAL],
issuer: ISSUER,
providerUrl: PROVIDER_URL,
});

const getVerifyRecordsRequest = (
chainId: number = CHAIN_ID
): VerifyRecordsRequest => ({
const getVerifyRecordsRequest = (): VerifyRecordsRequest => ({
ens: [ENS],
chainId,
providerUrl: PROVIDER_URL,
credentials: [SOCIAL_CREDENTIAL],
issuer: ISSUER,
});
Expand All @@ -61,6 +60,7 @@ describe('Verify records controller integration tests', () => {
let app: INestApplication;
let verifyRecordsService: DeepMocked<IVerifyRecordsService>;
let verifyRecordsControllerMapper: DeepMocked<IVerifyRecordsControllerMapper>;
let fetchChainIdService: DeepMocked<IFetchChainIdService>;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -70,6 +70,10 @@ describe('Verify records controller integration tests', () => {
provide: APP_FILTER,
useClass: filter,
})),
{
provide: FETCH_CHAIN_ID_SERVICE,
useValue: createMock<IFetchChainIdService>(),
},
{
provide: VERIFY_RECORDS_SERVICE,
useValue: createMock<IVerifyRecordsService>(),
Expand All @@ -89,9 +93,14 @@ describe('Verify records controller integration tests', () => {
DeepMocked<IVerifyRecordsControllerMapper>
>('VERIFY_RECORDS_CONTROLLER_MAPPER');

fetchChainIdService = module.get<DeepMocked<IFetchChainIdService>>(
FETCH_CHAIN_ID_SERVICE
);

verifyRecordsControllerMapper.mapVerifyRecordsApiRequestToVerifyRecordsRequest.mockReturnValue(getVerifyRecordsRequest());
verifyRecordsControllerMapper.mapVerifyRecordsResponsesToVerifyRecordsApiResponses.mockReturnValue([getVerifyRecordsApiResponse()])

fetchChainIdService.getChainId.mockResolvedValueOnce(CHAIN_ID);

app = module.createNestApplication();
await app.init();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"crypto": "^1.0.1",
"crypto-js": "^4.2.0",
"ens-did-resolver": "^1.0.4",
"ethers": "^6.13.2",
"ethers": "^6.13.4",
"ethr-did-resolver": "^10.1.10",
"express-session": "^1.18.0",
"moment": "^2.30.1",
Expand Down
Loading

0 comments on commit 8ba0d17

Please sign in to comment.