-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vc-api): external subdomainRecordsFetcher error handling
- Loading branch information
1 parent
98e6096
commit c337b45
Showing
5 changed files
with
92 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
apps/vc-api/src/api/filters/verify-records/records-fetching.filter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { BaseExceptionFilter } from '@nestjs/core'; | ||
import { ArgumentsHost, Catch, HttpStatus } from '@nestjs/common'; | ||
import { RecordsFetchingException } from '../../../core/domain/exceptions/RecordsFetching.exception'; | ||
|
||
@Catch(RecordsFetchingException) | ||
export class RecordsFetchingExceptionFilter extends BaseExceptionFilter { | ||
catch(exception: RecordsFetchingException, host: ArgumentsHost) { | ||
const context = host.switchToHttp(); | ||
const response = context.getResponse(); | ||
const httpStatus = HttpStatus.BAD_REQUEST; | ||
|
||
response.status(httpStatus).json({ | ||
message: exception.message, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
apps/vc-api/src/core/domain/exceptions/RecordsFetching.exception.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export class RecordsFetchingException extends Error { | ||
constructor(message: string) { | ||
super(message); | ||
} | ||
|
||
static forSubnamesWithProvider(subnames: string[], providerUrl: string) { | ||
const errorMessage = `Failed to fetch records for: ${subnames.join( | ||
', ' | ||
)}, with provider: ${providerUrl}`; | ||
return new RecordsFetchingException(errorMessage); | ||
} | ||
} |
118 changes: 60 additions & 58 deletions
118
apps/vc-api/src/external/subname-records-fetcher/subname-records.fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,115 @@ | ||
import { ISubnameRecordsFetcher } from '../../core/applications/verify-records/isubname-records.fetcher'; | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { Injectable } from '@nestjs/common'; | ||
import { Subname } from '../../core/domain/entities/subname'; | ||
import { ENVIRONMENT_GETTER, IEnvironmentGetter } from '../../core/applications/environment/ienvironment.getter'; | ||
import { ENS_MANAGER_SERVICE, IEnsManagerService } from '../../core/applications/ens-manager/iens-manager.service'; | ||
import { ChainId } from '../../core/domain/entities/environment'; | ||
import { GetRecordsResponse } from '../../core/applications/ens-manager/responses/get-records.response'; | ||
import { getRecords, batch } from '@ensdomains/ensjs/public'; | ||
import {createClient, http} from "viem"; | ||
import {mainnet, sepolia} from "viem/chains"; | ||
import { createClient, http } from 'viem'; | ||
import { mainnet, sepolia } from 'viem/chains'; | ||
import { RecordsFetchingException } from '../../core/domain/exceptions/RecordsFetching.exception'; | ||
|
||
@Injectable() | ||
export class SubnameRecordsFetcher implements ISubnameRecordsFetcher { | ||
constructor( | ||
@Inject(ENVIRONMENT_GETTER) private readonly environmentGetter: IEnvironmentGetter, | ||
@Inject(ENS_MANAGER_SERVICE) private readonly ensManagerService: IEnsManagerService | ||
) {} | ||
constructor() {} | ||
|
||
async fetchRecords(providerUrl: string, subname: string, chainId: ChainId, texts?: string[]): Promise<Subname> { | ||
let records: GetRecordsResponse; | ||
|
||
if(texts) { | ||
async fetchRecords( | ||
providerUrl: string, | ||
subname: string, | ||
chainId: ChainId, | ||
texts?: string[] | ||
): Promise<Subname> { | ||
try { | ||
const client = createClient({ | ||
chain: chainId === 1 ? mainnet : sepolia, | ||
transport: http(providerUrl) | ||
transport: http(providerUrl), | ||
}); | ||
|
||
const recordsFromEns = await getRecords(client,{ | ||
const recordsFromEns = await getRecords(client, { | ||
name: subname, | ||
texts: texts, | ||
coins: ['eth'], | ||
contentHash: true | ||
contentHash: true, | ||
}); | ||
|
||
records = { | ||
const records: GetRecordsResponse = { | ||
...recordsFromEns, | ||
isJAN: false | ||
} | ||
} else{ | ||
records = await this.ensManagerService.getRecords({ | ||
ens: subname, | ||
chainId: chainId, | ||
}) | ||
} | ||
isJAN: false, | ||
}; | ||
|
||
return this.mapSubnameRecordsResponseToSubname(subname, records); | ||
return this.mapSubnameRecordsResponseToSubname(subname, records); | ||
} catch (error) { | ||
throw RecordsFetchingException.forSubnamesWithProvider( | ||
[subname], | ||
providerUrl | ||
); | ||
} | ||
} | ||
|
||
async fetchRecordsFromManySubnames(providerUrl: string, subnames: string[], chainId: ChainId, texts?: string[]): Promise<Subname[]> { | ||
let records: GetRecordsResponse[]; | ||
|
||
if(texts) { | ||
async fetchRecordsFromManySubnames( | ||
providerUrl: string, | ||
subnames: string[], | ||
chainId: ChainId, | ||
texts: string[] | ||
): Promise<Subname[]> { | ||
try { | ||
const client = createClient({ | ||
chain: chainId === 1 ? mainnet : sepolia, | ||
transport: http(providerUrl) | ||
transport: http(providerUrl), | ||
}); | ||
|
||
const batchRequests = subnames.map(name => | ||
const batchRequests = subnames.map((name) => | ||
getRecords.batch({ | ||
name, | ||
texts, | ||
coins: ['eth'], | ||
contentHash: true | ||
contentHash: true, | ||
}) | ||
); | ||
|
||
const recordsFromEns = await batch(client, ...batchRequests); | ||
|
||
records = subnames.map((_, index) => ({ | ||
...recordsFromEns[index], | ||
isJAN: false | ||
} | ||
)); | ||
const records: GetRecordsResponse[] = subnames.map((_, index) => ({ | ||
...recordsFromEns[index], | ||
isJAN: false, | ||
})); | ||
|
||
return this.mapSubnameRecordsResponsesToSubnameArray(subnames, records); | ||
|
||
// TODO: the following code is unnecessary, we should remove it | ||
} else { | ||
const promises = subnames.map(subname => | ||
this.ensManagerService.getRecords({ | ||
ens: subname, | ||
chainId: chainId, | ||
}) | ||
} catch (error) { | ||
throw RecordsFetchingException.forSubnamesWithProvider( | ||
subnames, | ||
providerUrl | ||
); | ||
|
||
const records = await Promise.all(promises); | ||
return this.mapSubnameRecordsResponsesToSubnameArray(subnames, records); | ||
} | ||
} | ||
|
||
mapSubnameRecordsResponseToSubname(subname: string, records: GetRecordsResponse): Subname { | ||
mapSubnameRecordsResponseToSubname( | ||
subname: string, | ||
records: GetRecordsResponse | ||
): Subname { | ||
return { | ||
subname: subname, | ||
metadata: { | ||
contentHash: records.contentHash?.decoded ?? '', | ||
addresses: records.coins.map(coin => ({ | ||
addresses: records.coins.map((coin) => ({ | ||
id: String(coin.id), | ||
coinType: coin.id, | ||
address: coin.value, | ||
metadataId: '' | ||
metadataId: '', | ||
})), | ||
textRecords: records.texts.map(text => ({ | ||
textRecords: records.texts.map((text) => ({ | ||
key: text.key, | ||
value: text.value | ||
})) | ||
} | ||
value: text.value, | ||
})), | ||
}, | ||
}; | ||
} | ||
|
||
mapSubnameRecordsResponsesToSubnameArray(subnames: string[], records: GetRecordsResponse[]): Subname[] { | ||
return subnames.map((subname, index) => this.mapSubnameRecordsResponseToSubname(subname, records[index])); | ||
mapSubnameRecordsResponsesToSubnameArray( | ||
subnames: string[], | ||
records: GetRecordsResponse[] | ||
): Subname[] { | ||
return subnames.map((subname, index) => | ||
this.mapSubnameRecordsResponseToSubname(subname, records[index]) | ||
); | ||
} | ||
} |