Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 62 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Get network traffic statistics from [vnStat](https://github.com/vergoh/vnstat).

## Example

### Callback style

```js
const vnstat = require( 'vnstat-dumpdb' )();

Expand All @@ -36,6 +38,28 @@ vnstat.getConfig( ( err, config ) => {
} );
```

### Promise style

```js
const vnstat = require( 'vnstat-dumpdb' )();

// Get traffic per day with async/await
async function getTraffic () {
try {
const data = await vnstat.getStats( 'eth0' );
console.log( data.traffic.days );

const config = await vnstat.getConfig();
console.log( `Interfaces updating every ${config.UpdateInterval} minutes` );
}
catch ( err ) {
console.log( err );
}
}

getTraffic();
```


## Installation

Expand All @@ -56,7 +80,11 @@ setting | type | default | description

## Callback & errors

Each method below takes a callback _function_ which gets two arguments:
Each method supports both **callback** and **Promise** patterns:

### Callback style

Each method takes an optional callback _function_ which gets two arguments:

* `err` - Instance of `Error` or `null`
* `data` - Result `object` or not set when error
Expand All @@ -73,6 +101,26 @@ const myCallback = ( err, data ) => {
};
```

### Promise style

If no callback is provided, the method returns a `Promise`:

```js
// Using async/await
try {
const data = await vnstat.getConfig();
console.log( data );
}
catch ( err ) {
console.log( err );
}

// Using .then() / .catch()
vnstat.getConfig()
.then( ( data ) => console.log( data ) )
.catch( ( err ) => console.log( err ) );
```


#### Errors

Expand All @@ -84,18 +132,21 @@ invalid interface | `iface` is invalid or not set up |



## getStats ( [iface], callback )
## getStats ( [iface], [callback] )

Get statistics for one, multiple or all interfaces.

* One: `vnstat.getStats( 'eth0', callback )`
* All: `vnstat.getStats( false, callback )`
* One: `vnstat.getStats( 'eth0', callback )` or `await vnstat.getStats( 'eth0' )`
* All: `vnstat.getStats( false, callback )` or `await vnstat.getStats()`


```js
// Get traffic for interface en1
// Get traffic for interface en1 (callback)
vnstat.getStats( 'en1', console.log );

// Get traffic for interface en1 (promise)
const data = await vnstat.getStats( 'en1' );

// Output
{ id: 'en1',
nick: 'en1',
Expand Down Expand Up @@ -146,11 +197,12 @@ vnstat.getStats( 'en1', console.log );
```


## getConfig ( callback )
## getConfig ( [callback] )

Get vnStat configuration.

```js
// Callback style
vnstat.getConfig( ( err, config ) => {
if ( err ) {
console.log( err );
Expand All @@ -159,6 +211,10 @@ vnstat.getConfig( ( err, config ) => {

console.log( `Interfaces updating every ${config.UpdateInterval} seconds` );
} );

// Promise style
const config = await vnstat.getConfig();
console.log( `Interfaces updating every ${config.UpdateInterval} seconds` );
```


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"url": "https://frankl.in"
},
"name": "vnstat-dumpdb",
"version": "2.0.3",
"version": "2.1.0",
"description": "Get vnStat network traffic and configuration",
"repository": {
"type": "git",
Expand Down
112 changes: 112 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@


dotest.add( 'Method .getStats - iface', ( test ) => {
vnstat.getStats( 'eth0', ( err, data ) => {

Check warning on line 36 in test.js

View workflow job for this annotation

GitHub Actions / Node (25)

Arrow function has a complexity of 15. Maximum allowed is 8

Check warning on line 36 in test.js

View workflow job for this annotation

GitHub Actions / Node (24)

Arrow function has a complexity of 15. Maximum allowed is 8
const days = data?.traffic?.days || data?.traffic?.day;
const rx = days?.[0]?.rx;
const hasData = days && days.length > 0;
Expand Down Expand Up @@ -102,5 +102,117 @@
} );


// Promise-based tests
dotest.add( 'Promise: .getConfig', async ( test ) => {
config.bin = './testing/mock-vnstat';
vnstat = app( config );

try {
const data = await vnstat.getConfig();
test()
.isObject( 'fail', 'data', data )
.isNotEmpty( 'fail', 'data.DatabaseDir', data?.DatabaseDir )
.isString( 'fail', 'data.UpdateInterval', data?.UpdateInterval )
.done();
}
catch ( err ) {
test( err ).done();
}
} );


dotest.add( 'Promise: .getStats - iface', async ( test ) => {

Check warning on line 124 in test.js

View workflow job for this annotation

GitHub Actions / Node (25)

Async arrow function has a complexity of 16. Maximum allowed is 8

Check warning on line 124 in test.js

View workflow job for this annotation

GitHub Actions / Node (24)

Async arrow function has a complexity of 16. Maximum allowed is 8
try {
const data = await vnstat.getStats( 'eth0' );
const days = data?.traffic?.days || data?.traffic?.day;
const rx = days?.[0]?.rx;
const hasData = days && days.length > 0;

const t = test()
.isObject( 'fail', 'data', data )
.isString( 'fail', 'data.id', data?.id || data?.name )
.isObject( 'fail', 'data.traffic', data?.traffic )
.isArray( 'fail', 'data.traffic.days', days );

if ( hasData ) {
t.isObject( 'fail', 'data.traffic.days[0]', days?.[0] )
.isNumber( 'fail', 'data.traffic.days[0].rx', rx );
}

t.done();
}
catch ( err ) {
test( err ).done();
}
} );


dotest.add( 'Promise: .getStats - all', async ( test ) => {
try {
const data = await vnstat.getStats();
test()
.isArray( 'fail', 'data', data )
.done();
}
catch ( err ) {
test( err ).done();
}
} );


dotest.add( 'Promise: Error - invalid interface', async ( test ) => {
try {
await vnstat.getStats( 'unreal-iface' );
test()
.fail( 'Should have thrown an error' )
.done();
}
catch ( err ) {
test()
.isError( 'fail', 'err', err )
.isExactly( 'fail', 'err.message', err?.message, 'invalid interface' )
.done();
}
} );


dotest.add( 'Promise: Error - no config', async ( test ) => {
config.bin = '-';
vnstat = app( config );

try {
await vnstat.getConfig();
test()
.fail( 'Should have thrown an error' )
.done();
}
catch ( err ) {
test()
.isError( 'fail', 'err', err )
.isExactly( 'fail', 'err.message', err?.message, 'no config' )
.done();
}
} );


dotest.add( 'Promise: Error - command failed', async ( test ) => {
config.bin = '-';
vnstat = app( config );

try {
await vnstat.getStats();
test()
.fail( 'Should have thrown an error' )
.done();
}
catch ( err ) {
test()
.isError( 'fail', 'err', err )
.isExactly( 'fail', 'err.message', err?.message, 'command failed' )
.done();
}
} );


// Start the tests
dotest.run();
47 changes: 40 additions & 7 deletions vnstat-dumpdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,44 @@ function doError ( message, err, details, callback ) {
}


/**
* Helper to promisify a function that uses callbacks
*
* @param {function} fn The function to promisify
* @returns {function} Wrapped function that returns void if callback provided, or Promise if not
*/

function promisify ( fn ) {
return function ( ...args ) {
const callback = args[args.length - 1];

// If last argument is a function, use callback style
if ( typeof callback === 'function' ) {
return fn.apply( this, args );
}

// Otherwise, return a Promise
return new Promise( ( resolve, reject ) => {
args.push( ( err, data ) => {
if ( err ) {
reject( err );
}
else {
resolve( data );
}
} );

fn.apply( this, args );
} );
};
}


/**
* Get vnStat config
*
* @param {function} callback `(err, data)`
* @returns {void}
* @param {function} [callback] `(err, data)`
* @returns {void|Promise}
* @callback callback
*/

Expand Down Expand Up @@ -72,9 +105,9 @@ function getConfig ( callback ) {
/**
* Get stats database
*
* @param {string} [iface] Limit data to one interface
* @param {function} callback `(err, data)`
* @returns {void}
* @param {string} [iface] Limit data to one interface
* @param {function} [callback] `(err, data)`
* @returns {void|Promise}
* @callback callback
*/

Expand Down Expand Up @@ -138,8 +171,8 @@ module.exports = ( {
set.iface = iface;

return {
getStats,
getConfig,
getStats: promisify( getStats ),
getConfig: promisify( getConfig ),
set,
};

Expand Down