1.0.2 • Published 3 years ago

@huzapi/error-handler v1.0.2

Weekly downloads
-
License
ISC
Repository
gitlab
Last release
3 years ago

Huz Api / Error Handler

Error Handler with multi-language/multi-tenancy support (Exception Driven Development)

  • TDD (Test driven)
  • Fully JsDoc
  • index.d.ts included (built-in @types support)

Install to your project

npm i @huzapi/error-handler

Builders

Custom/Free Usage

const {ErrorInstance} = require('@huzapi/error-handler');
//custom errors with exception-name
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty', 411, 'Game').error();
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty', 411).error();
ErrorInstance.custom(req, 'NameEmptyError', 'name is empty').error(); // status: 400
//custom errors without exception-name
ErrorInstance.anonymous(req, 'name is empty', 411).error(); //name: UnknownError
ErrorInstance.anonymous(req, 'name is empty').error(); // name: UnknownError, status: 400

Native ( With Try/Catch) Usage

const {ErrorInstance} = require('@huzapi/error-handler');
try {
    //....
} catch (e) {
    ErrorInstance.native(req, e, 'Game').error(); //if e has not status, status: 400
    //or
    ErrorInstance.native(req, e).error(); //if e has not status, status: 400
}

Multi-Language & Multi Tenancy Usage

const {ErrorInstance} = require('@huzapi/error-handler');
//without tenancy
ErrorInstance.appendDictionary(
    {
        "en": {
            "BannedCountryError": "Country {{country}} is banned for this page!"
        }       
    }, false
);
//with tenancy
ErrorInstance.appendDictionary(
    {
        "en": {
            "BannedCountryError": {
                "$default": "Country {{country}} is banned for this page!",
                "project1": "Our Foobar game is banned at {{country}}"
            }       
        }       
    }, true
);
ErrorInstance.i18n(req, 'BannedCountryError', {country: 'Germany'}, 403).error();

Action & Severity

Severities

TypeUsage
emergencyErrorInstance.anonymous(req, 'Databese server down!').emergency();
alertErrorInstance.anonymous(req, 'disk usage is 90%').alert();
criticalErrorInstance.anonymous(req, 'remote api does not work').critical();
errorErrorInstance.anonymous(req, 'name is empty').error();
warningErrorInstance.anonymous(req, 'gender is empty').warning();
noticeErrorInstance.anonymous(req, 'endpoint duration is too high').notice();
infoErrorInstance.anonymous(req, 'endpoint duration is 30msec').info();
debugErrorInstance.anonymous(req, 'hello world').debug();

Actions

Note: Each action runs once (only 1, successive events will be ignored) at a context scope

TypeDefault ScopeInfo
displayemergency,alert,critical,error,warning,notice,info,debugconsole.log(...)
logemergency,alert,critical,error,warning,noticelogs exception if ErrorInterception.actionLog is set
mailemergency,alert,criticalsends email if ErrorInterception.actionMail is set
smsemergency,alertsends SMS if ErrorInterception.actionSms is set
raiseemergency,alert,critical,errorthrow ...

Action x Severity Matrix

//you can set each action for each severity level and environment
//if you dont set, default scopes will be used
const {ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();
if (process.env.NODE_ENV === 'production') {
    //parameters: display?: boolean,  log?: boolean,  raise?: boolean,  mail?: boolean,  sms?: boolean
    interceptor.setSeverity('emergency', true, true, true, true, true);
    ///.... for all severity types
    interceptor.setSeverity('debug', false, false, false, false, false);
} else {
    //...
}

Easy Actions

You can call each action with by-pass severity-matrix above

TypeUsage
displayErrorInstance.anonymous(req, 'name is empty').display().error();
logErrorInstance.anonymous(req, 'name is empty').log().error();
mailErrorInstance.anonymous(req, 'name is empty').mail().error();
smsErrorInstance.anonymous(req, 'name is empty').sms().error();

Action Callbacks

//you can define logger, mail sender, sms sender, display format and response-format
//if you dont set, default scopes will be used
const {ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();

// callback must be Promise
// If not set, no-log
interceptor.actionLog(async (err) => {
    //You can decorate this part, err: ErrorInstance
    await YourMongoLogger.add(err);
} );

// callback must be Promise
// If not set, console.log(Partial<ErrorInstance>)
interceptor.actionDisplay(async (err) => {
    //You can decorate this part, err: ErrorInstance
    console.log(err.message);
} );

// callback must be Promise
// If not set, no-mail-send
interceptor.actionMail(async (err) => {
    //You can decorate this part, err: ErrorInstance
    await YourMailSender.add(err);
} );

// callback must be Promise
// If not set, no-sms-send
interceptor.actionSms(async (err) => {
    //You can decorate this part, err: ErrorInstance
    await YourSmsSender.add(err);
} );

// callback must NOT be Promise
// If not set, api response is {status, name, message, track, holder}
interceptor.responseFormatter((err) => {
    //You can decorate this part or you can convert to array etc, err: ErrorInstance
    return [{code: err.name, message: err.message}];
} );

Multi-Language (I18N), Dictionary & Multi-Tenancy

Multi-Language and Dictionary

You must set dictionary with given format (below) You must set current user's language callback If current user's text has not defined at dictionary, first fixed key is taken from another language If current tenant's text has not defined at dictionary, first fixed key is taken from default tenant or another language

const {ErrorInstance, ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();

//without tenancy /*Format {

[language: string]: {
    [error-name: string]: string;
}

} */ ErrorInstance.appendDictionary( { "en": { "BannedCountryError": "Country {{country}} is banned for this page!", "anotherError": "Another message template" }, "another-language": { "BannedCountryError": "...... {{country}} ......", "anotherError": "......" } }, false );

// callback must NOT be Promise // callback must return string // If not set, first language key at dictionary will be taken interceptor.currentLanguage((err) => { //You can decorate this part, err: ErrorInstance, err.$req: Express.Request return err.$req._userLanguage; // it should be same format with language-code at dictionary which you set //or you can return static ==> return 'en'; } );

### Multi-Tenancy
> If you need to change error messages tenant by tenant, or if you want to specialize some error messages to some tenants
> You can set dictionary with tenant layer and write callback for current tenant
>
````javascript
const {ErrorInstance, ErrorInterception} = require('@huzapi/error-handler');
const interceptor = new ErrorInterception();

//with tenancy
/*Format 
{
    [language: string]: {
        [error-name: string]: {
            [tenant-key]: string;
        }
    }
}
*/
ErrorInstance.appendDictionary(
    {
        "en": {
            "$default": {
                "BannedCountryError": "Country {{country}} is banned for this page!",
                "anotherError": "Another message template"
            },
            "tenant66": {
                "BannedCountryError": "......  {{country}} ......",
            }
        },
        "another-language": {
            "$default": {
                "BannedCountryError": "......  {{country}} ......",
                "anotherError": "......"
            },
            "tenant66": {
                "BannedCountryError": "......  {{country}} ......",
            }
        }
    }, true
);

// callback must NOT be Promise
// callback must return string
// If not set, default tenant at dictionary will be taken
interceptor.currentTenant((err) => {
    //You can decorate this part, err: ErrorInstance, err.$req: Express.Request
    return err.$req._tenantId; // it should be same format with tenant-key at dictionary which you set
    //or you can return static, example ==> return 'master';
} );

Named Errors

You can define your named error classes, like Java, Php

  • file: UserNotFoundError.js
const {ErrorInstance} = require('@huzapi/error-handler');

class UserNotFoundError extends ErrorInstance { constructor(req, id) { this.name = this.constructor.name; this.status = 404; this.severity = 'critical'; this.holder = 'User'; this.$req = req;

    //if multi-language or dictionary usage
    this.parameters = {id: id};
    this.refreshMessage();//todo
    //else
    this.message = `User not found with id: ${id}`;
    
    //call actions you need
    // or you can check matrix if you apply your settings, @todo
    this.display(); //your setting at this.getSeveritySetting().display;
    this.log(); //your setting at this.getSeveritySetting().log;
    this.mail(); //your setting at this.getSeveritySetting().mail;
    this.sms(); //your setting at this.getSeveritySetting().sms;
    
    //IMPORTANT you dont call severity methods,
    //  ie: emergency(), alert(), critical(), error(), warning(), notice(), info(), debug()
    //IMPORTANT you dont call raise();
}

} module.exports = UserNotFoundError;

- file: another.js
````javascript
const {UserNotFoundError} = require('path-to/UserNotFoundError');

throw new UserNotFoundError(req, 4567);

HTTP Errors

You can use this package at express routing

const {ErrorInstance} = require('@huzapi/error-handler');

router.get('games', (req, res) => {
    yourPromiseFunction(req, req.query.id).then(content => {
        res.json(content).send();            
    }).catch(e => {
        ErrorInstance.send(req, res, e, 'Game'); //response based on your responseFormatter interceptor
    });
});

TODO

Dictionary, Multi-Language and Multi-Tenancy features were not developed yet :(

1.0.2

3 years ago

1.0.1

3 years ago