diff --git a/src/api/openapi.js b/src/api/openapi.js index 085ee2e..a71cd7d 100644 --- a/src/api/openapi.js +++ b/src/api/openapi.js @@ -131,6 +131,16 @@ module.exports = { } } } + }, + 400: { + description: 'Validation Error', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ValidationError' + } + } + } } } } @@ -184,6 +194,16 @@ module.exports = { } } } + }, + 400: { + description: 'Validation Error', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ValidationError' + } + } + } } } } @@ -237,6 +257,16 @@ module.exports = { } } } + }, + 400: { + description: 'Validation Error', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ValidationError' + } + } + } } } } @@ -341,6 +371,16 @@ module.exports = { } } } + }, + 400: { + description: 'Validation Error', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ValidationError' + } + } + } } } } @@ -402,6 +442,16 @@ module.exports = { } } } + }, + 400: { + description: 'Validation Error', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ValidationError' + } + } + } } } } @@ -1281,6 +1331,18 @@ module.exports = { lastUpdated: { type: 'string', format: 'date-time' } } } + }, + ValidationError: { + type: 'object', + properties: { + errors: { + type: 'array', + items: { + type: 'string', + example: 'An address is invalid' + } + } + } } } } diff --git a/src/controller/httpsAPI.ts b/src/controller/httpsAPI.ts index 3bd733d..7e0b964 100644 --- a/src/controller/httpsAPI.ts +++ b/src/controller/httpsAPI.ts @@ -10,6 +10,7 @@ import BitcoinRouter from '../service/bitcoin/BitcoinRouter' import { ValidationError, object, string } from 'yup' import { utils } from 'ethers' import { AddressService } from '../service/address/AddressService' +import { supportedFiat } from '../coinmarketcap/support' interface HttpsAPIDependencies { app: Express, @@ -49,12 +50,19 @@ export class HttpsAPI { .oneOf(Object.keys(this.dataSourceMapping), 'The current chainId is not supported') }) const addressSchema = object({ - address: string().required('we required a valid address') + address: string().required('An address is invalid') .trim() .transform(address => utils.isAddress(address.toLowerCase()) ? address : '') }).required() + const currencySchema = object({ + convert: string().optional() + .trim() + .oneOf(supportedFiat, 'The current currency is not supported') + }) - const whilelist = ['https://dapp.testnet.dao.rif.technology', 'https://dapp.mainnet.dao.rif.technology'] + const whilelist = ['https://dapp.testnet.dao.rif.technology', + 'https://dapp.mainnet.dao.rif.technology', + 'https://rif-wallet-services.testnet.rifcomputing.net'] this.app.use(cors({ origin: (origin, callback) => { if (!origin || whilelist.indexOf(origin) !== -1) { @@ -135,15 +143,18 @@ export class HttpsAPI { this.app.get( '/price', - async (req: Request<{}, {}, {}, PricesQueryParams>, res: Response, next: NextFunction) => { + async (req: Request<{}, {}, {}, PricesQueryParams>, res: Response) => { try { + const { convert = 'USD', addresses = '' } = req.query + currencySchema.validateSync({ convert }) + addresses.split(',').forEach(address => addressSchema.validateSync({ address })) const prices = await this.addressService.getPrices({ - addresses: req.query.addresses || '', - convert: req.query.convert || 'USD' + addresses, + convert }) return this.responseJsonOk(res)(prices) } catch (error) { - next(error) + this.handleValidationError(error, res) } } ) @@ -162,10 +173,12 @@ export class HttpsAPI { this.app.get( '/address/:address', - async (req, res, next: NextFunction) => { + async (req, res) => { try { const { limit, prev, next, chainId = '31', blockNumber = '0' } = req.query const { address } = req.params + chainIdSchema.validateSync({ chainId }) + addressSchema.validateSync({ address }) const data = await this.addressService.getAddressDetails({ chainId: chainId as string, address, @@ -176,7 +189,7 @@ export class HttpsAPI { }) return this.responseJsonOk(res)(data) } catch (error) { - next(error) + this.handleValidationError(error, res) } } ) diff --git a/src/service/address/AddressService.ts b/src/service/address/AddressService.ts index 560e8b9..5fc381b 100644 --- a/src/service/address/AddressService.ts +++ b/src/service/address/AddressService.ts @@ -89,7 +89,7 @@ export class AddressService { } async getPrices ({ addresses, convert }: GetPricesFunction) { - const addressesArr = addresses.split(',') + const addressesArr = addresses.toLowerCase().split(',') return this.lastPrice.getPrices(addressesArr, convert) } diff --git a/test/api.test.ts b/test/api.test.ts index b153105..28539e5 100644 --- a/test/api.test.ts +++ b/test/api.test.ts @@ -98,9 +98,13 @@ describe('coin market cap', () => { ] const res = await request(app) .get(`/price?convert=asd&addresses=${addresses.join(',')}`) - .expect(500) + .expect(400) - expect(res.text).toEqual('Convert not supported') + expect(res.body).toEqual({ + errors: [ + 'The current currency is not supported' + ] + }) expect(axiosMock.get).toHaveBeenCalledTimes(1) }) @@ -110,9 +114,13 @@ describe('coin market cap', () => { const res = await request(app) .get('/price?convert=USD&addresses=0x2acc95758f8b5f583470ba265eb685a8f45fc9d') - .expect(200) + .expect(400) - expect(res.text).toEqual('{}') + expect(res.body).toEqual({ + errors: [ + 'An address is invalid' + ] + }) expect(axiosMock.get).toHaveBeenCalledTimes(1) }) })