diff --git a/application/api/.eslintrc.json b/application/api/.eslintrc.json index 4f0fbb07..bb85d170 100644 --- a/application/api/.eslintrc.json +++ b/application/api/.eslintrc.json @@ -10,6 +10,7 @@ "db": "readonly", "bus": "readonly", "domain": "readonly", - "metarhia": "readonly" + "metarhia": "readonly", + "DomainError": "readonly" } } diff --git a/application/api/auth.2/error.js b/application/api/auth.2/error.js new file mode 100644 index 00000000..d658cb99 --- /dev/null +++ b/application/api/auth.2/error.js @@ -0,0 +1,16 @@ +({ + AuthError: class AuthError extends DomainError { + static ALREADY_EXISTS() { + return new AuthError(409, { message: 'User already exists' }); + } + static INVALID_CREDENTIALS() { + return new AuthError(400, { message: 'Incorrect logic or password' }); + } + }, + onError(error) { + const { AuthError } = api.auth.error; + if (error instanceof AuthError) return error; + // handle unexpected, non domain error and return based on error handling logic + return error; + }, +}); diff --git a/application/api/auth.2/register.js b/application/api/auth.2/register.js index 1547ff22..6ac6ef9e 100644 --- a/application/api/auth.2/register.js +++ b/application/api/auth.2/register.js @@ -1,8 +1,10 @@ ({ access: 'public', method: async ({ login, password, fullName }) => { + const { AuthError } = api.auth.error; const hash = await metarhia.metautil.hashPassword(password); - await api.auth.provider.registerUser(login, hash, fullName); + const created = await api.auth.provider.registerUser(login, hash, fullName); + if (!created) return AuthError.ALREADY_EXISTS(); const token = await context.client.startSession(); return { status: 'success', token }; }, diff --git a/application/api/auth.2/signin.js b/application/api/auth.2/signin.js index 742ce384..d3c61632 100644 --- a/application/api/auth.2/signin.js +++ b/application/api/auth.2/signin.js @@ -1,11 +1,12 @@ ({ access: 'public', method: async ({ login, password }) => { + const { AuthError } = api.auth.error; const user = await api.auth.provider.getUser(login); - if (!user) throw new Error('Incorrect login or password'); + if (!user) return AuthError.INVALID_CREDENTIALS(); const { accountId, password: hash } = user; const valid = await metarhia.metautil.validatePassword(password, hash); - if (!valid) throw new Error('Incorrect login or password'); + if (!valid) return AuthError.INVALID_CREDENTIALS(); console.log(`Logged user: ${login}`); const token = api.auth.provider.generateToken(); const data = { accountId: user.accountId }; diff --git a/application/api/example.1/add.js b/application/api/example.1/add.js index 080f3d2e..ec791eed 100644 --- a/application/api/example.1/add.js +++ b/application/api/example.1/add.js @@ -5,9 +5,20 @@ }, method: async ({ a, b }) => { + const { ExampleError } = api.example.error; + if (typeof a !== 'number') return ExampleError.INVALID_ARG_A(); + if (typeof b !== 'number') return ExampleError.INVALID_ARG_B(); const result = a + b; return result; }, returns: 'number', + + onError(error) { + const { ExampleError } = api.example.error; + // in theory there's no need to trigger onError handler + if (error instanceof ExampleError) return error; + // handle unexpected, non domain error and return based on error handling logic + return error; + }, }); diff --git a/application/api/example.1/citiesByCountry.js b/application/api/example.1/citiesByCountry.js index 95c89f8f..b9e8fb9e 100644 --- a/application/api/example.1/citiesByCountry.js +++ b/application/api/example.1/citiesByCountry.js @@ -1,4 +1,6 @@ async ({ countryId }) => { + const { ExampleError } = api.example.error; + if (!countryId) return ExampleError.MISSING_COUNTRY_ID(); const fields = ['cityId', 'name']; const where = { countryId }; const data = await db.pg.select('City', fields, where); diff --git a/application/api/example.1/error.js b/application/api/example.1/error.js index e89dd604..a8ae9007 100644 --- a/application/api/example.1/error.js +++ b/application/api/example.1/error.js @@ -1 +1,13 @@ -async () => new Error('Return error'); +({ + ExampleError: class ExampleError extends DomainError { + static INVALID_ARG_A() { + return new ExampleError(400, { message: 'Invalid argument: a' }); + } + static INVALID_ARG_B() { + return new ExampleError(400, { message: 'Invalid argument: b' }); + } + static MISSING_COUNTRY_ID() { + return new ExampleError(400, { message: 'Missing country id' }); + } + }, +}); diff --git a/types/global.d.ts b/types/global.d.ts index c30a8220..c9569bd3 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -23,3 +23,27 @@ declare global { const pg: Database; } } + +export interface ErrorOptions { + code?: number | string; + cause?: Error; +} + +export class Error extends global.Error { + constructor(message: string, options?: number | string | ErrorOptions); + message: string; + stack: string; + code?: number | string; + cause?: Error; +} + +type Errors = Record; + +export class DomainError extends Error { + constructor(code?: string, options?: number | string | ErrorOptions); + message: string; + stack: string; + code?: number | string; + cause?: Error; + toError(errors: Errors): Error; +}