Skip to content

Commit

Permalink
feat: handle response error
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrsitarz committed Dec 18, 2022
1 parent cb43706 commit 2e30969
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 21 deletions.
86 changes: 73 additions & 13 deletions src/base/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import { PersonaAnalysisConfig, RequestInit } from '../types';
import {
PersonaAnalysisConfig,
RequestInit,
ResponseError as ResponseErrorType,
ResponseErrorTitle,
ResponseErrorStatus,
} from '../types';
import { isWalletAddressValid, areWalletAddressesValid } from '../utils';

export class ResponseError implements ResponseErrorType {
type: string;
title: ResponseErrorTitle;
status: ResponseErrorStatus;
description?: string;

constructor(responseError: ResponseErrorType) {
this.type = responseError.type;
this.title = responseError.title;
this.status = responseError.status;
this.description = responseError.description;
}
}

export abstract class Base {
private apiKey: string;
private baseUrl: string;
Expand All @@ -10,7 +30,31 @@ export abstract class Base {
this.baseUrl = config.baseUrl || 'https://api.persona.staging.cookie3.co';
}

protected async request<T>(endpoint: string, options: RequestInit): Promise<T> {
private handleError(error: Error): ResponseErrorType {
try {
const { type, title, status, description }: ResponseErrorType = JSON.parse(error.message);

return new ResponseError({
type,
title,
status,
description,
});
} catch (e) {
// If the error message is not a valid JSON string, return a default error object
return new ResponseError({
type: 'https://httpstatuses.com/500',
title: 'Internal Server Error',
status: 500,
description: 'An internal server error occurred',
});
}
}

protected async request<T>(
endpoint: string,
options: RequestInit,
): Promise<T | ResponseErrorType> {
const url = `${this.baseUrl}${endpoint}`;
const body = JSON.parse(options.body);
const walletAddress = body.walletAddress;
Expand All @@ -25,16 +69,32 @@ export abstract class Base {
headers,
};

const validateWalletAddresses = Promise.resolve(
!!walletAddress ? isWalletAddressValid(walletAddress) : areWalletAddressesValid(body),
);

const areValid = await validateWalletAddresses;
return areValid
? fetch(url, config)
.then((response) => response.text())
.then((result) => result as T)
.catch((error) => error)
: 'incorrect wallet address, please ensure that any wallet address you provide is correct';
const validateWalletAddresses = !!walletAddress
? isWalletAddressValid(walletAddress)
: areWalletAddressesValid(body);

const areWalletsValid = validateWalletAddresses;

if (!areWalletsValid) {
return new ResponseError({
type: 'https://httpstatuses.com/422',
title: 'Unprocessable Entity',
status: 422,
description:
'incorrect wallet address, please ensure that any wallet address you provide is correct',
});
}

try {
const response = await fetch(url, config);

if (response.status === 401) {
throw new Error(JSON.stringify(await response.json()));
}

return response.json() as T;
} catch (error: any) {
return this.handleError(error);
}
}
}
12 changes: 10 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import PersonaAnalysis from './personaAnalysis';
import { PersonaAnalysis } from './personaAnalysis';
import { ResponseError } from './base';
import { isWalletAddressValid, areWalletAddressesValid, isResponseError } from './utils';

export default PersonaAnalysis;
export {
PersonaAnalysis,
ResponseError,
isWalletAddressValid,
areWalletAddressesValid,
isResponseError,
};
6 changes: 3 additions & 3 deletions src/personaAnalysis/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Base } from '../base';
import { PersonaAnalysisStatus, HttpMethod, WalletsForPersona, ResponseError } from '../types';

export default class PersonaAnalysis extends Base {
// Returns information if provided wallet address meet rules for given persona
export class PersonaAnalysis extends Base {
//Returns information if provided wallet address meet rules for given persona
isPersonaMatched(
walletAddress: string,
personaId: string,
Expand All @@ -12,7 +12,7 @@ export default class PersonaAnalysis extends Base {
body: JSON.stringify({ walletAddress, personaId }),
});
}
// Returns wallets that meet the rules in all personas attached to account
//Returns wallets that meet the rules in all personas attached to account
filterMatchedPersonas(walletAddresses: string[]): Promise<WalletsForPersona | ResponseError> {
return this.request('/personaAnalysis/personaArray', {
method: HttpMethod.POST,
Expand Down
11 changes: 9 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,18 @@ interface WalletsForPersona {
walletsForPersona: PersonaWallet[];
}

type ResponseErrorTitle = 'Unauthorized' | 'Too Many Requests';
type ResponseErrorStatus = 401 | 429;
type ResponseErrorTitle =
| 'Unauthorized'
| 'Too Many Requests'
| 'Unprocessable Entity'
| 'Internal Server Error';
type ResponseErrorStatus = 401 | 422 | 429 | 500;

interface ResponseError {
type: string;
title: ResponseErrorTitle;
status: ResponseErrorStatus;
description?: string;
}

export type {
Expand All @@ -55,6 +60,8 @@ export type {
PersonaAnalysisStatus,
WalletsForPersona,
ResponseError,
ResponseErrorTitle,
ResponseErrorStatus,
};

export { HttpMethod };
11 changes: 11 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { ResponseError } from '../base';
import {
ResponseError as ResponseErrorType,
PersonaAnalysisStatus,
WalletsForPersona,
} from '../types';

export const isWalletAddressValid = (walletAddress: string) => {
const regex = new RegExp(/^0x[a-fA-F0-9]{40}$/);

Expand All @@ -6,3 +13,7 @@ export const isWalletAddressValid = (walletAddress: string) => {

export const areWalletAddressesValid = (walletAddresses: string[]) =>
!walletAddresses.find((walletAddress) => !isWalletAddressValid(walletAddress));

export const isResponseError = (
response: ResponseErrorType | PersonaAnalysisStatus | WalletsForPersona,
): response is ResponseError => response instanceof ResponseError;
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"allowSyntheticDefaultImports": true
"allowSyntheticDefaultImports": true,
"moduleResolution": "node"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
Expand Down

0 comments on commit 2e30969

Please sign in to comment.