From c0ee4166cd361a0c69ebed6b498f10469c8a5e83 Mon Sep 17 00:00:00 2001 From: oof1lab Date: Sat, 6 Jan 2018 16:43:22 +0000 Subject: [PATCH] Feature : #14 : _health and _version --- README.md | 63 +++++++++++++++++++++++++++++++++++----- index.js | 20 ++++++------- lib/util/cmd.helper.js | 4 ++- lib/xapi.js | 55 ++++++++++++++++++++++++++++++++++- package-lock.json | 7 ++++- package.json | 3 +- tests/tests.js | 66 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 195 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8b4485bf6b..b55be97b65 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Powered by popular node packages : ([express](https://github.com/expressjs/expre * Upload single file * Upload multiple files * Download file - +* Health and version apis Use HTTP clients like [Postman](https://www.getpostman.com/) or [similar tools](https://chrome.google.com/webstore/search/http%20client?_category=apps) to invoke REST API calls @@ -121,15 +121,16 @@ if you haven't on your system. | GET :fire:| [/api/tableName/groupby](#group-by-having-as-api) | Group by results of column(s) | | GET :fire:| [/api/tableName/ugroupby](#union-of-multiple-group-by-statements) | Multiple group by results using one call | | GET :fire:| [/api/tableName/chart](#chart) | Numeric column distribution based on (min,max,step) or(step array) or (automagic)| -| GET :fire:| [/api/tableName/autochart](#autochart) | Same as Chart but identifies which are numeric column automatically - gift for lazy while prototyping| -| GET :fire:| [/api/xjoin](#xjoin) | handles join | +| GET :fire:| [/api/tableName/autochart](#autochart) | Same as Chart but identifies which are numeric column automatically - gift for lazy while prototyping| +| GET :fire:| [/api/xjoin](#xjoin) | handles join | | GET :fire:| [/dynamic](#run-dynamic-queries) | execute dynamic mysql statements with params | | GET :fire:| [/upload](#upload-single-file) | upload single file | | GET :fire:| [/uploads](#upload-multiple-files) | upload multiple files | | GET :fire:| [/download](#download-file) | download a file | -| GET | /api/tableName/describe| describe each table for its columns | -| GET | /api/tables| get all tables in database | - +| GET | /api/tableName/describe | describe each table for its columns | +| GET | /api/tables | get all tables in database | +| GET | [/_health](#health) | gets health of process and mysql -- details query params for more details | +| GET | [/_version](#version) | gets version of Xmysql, mysql, node| ## Relational Tables @@ -841,7 +842,55 @@ returns uploaded file names as string http://localhost:3000/download?name=fileName > For upload and download of files -> you can specify storage folder using -s option -> Upload and download apis are available only with local mysql server +> Upload and download apis are available only with local mysql server + +## Health +[:arrow_heading_up:](#api-overview) + +http://localhost:3000/_health + +``` +{"process_uptime":3.858,"mysql_uptime":"2595"} +``` + +Shows up time of Xmysql process and mysql server + + +http://localhost:3000/_health?details=1 + +``` +{"process_uptime":1.151,"mysql_uptime":"2798", +"os_total_memory":17179869184, +"os_free_memory":2516357120, +"os_load_average":[2.29931640625,2.1845703125,2.13818359375], +"v8_heap_statistics":{"total_heap_size":24735744, +"total_heap_size_executable":5242880, +"total_physical_size":23521048, +"total_available_size":1475503064, +"used_heap_size":18149064, +"heap_size_limit":1501560832, +"malloced_memory":8192, +"peak_malloced_memory":11065664, +"does_zap_garbage":0}} +``` + +Provides more details on process. + +Infact passing any query param gives detailed health output: example below + +http://localhost:3000/_health?voila +``` +{"process_uptime":107.793,"mysql_uptime":"2905","os_total_memory":17179869184,"os_free_memory":2573848576,"os_load_average":[2.052734375,2.12890625,2.11767578125],"v8_heap_statistics":{"total_heap_size":24735744,"total_heap_size_executable":5242880,"total_physical_size":23735016,"total_available_size":1475411128,"used_heap_size":18454968,"heap_size_limit":1501560832,"malloced_memory":8192,"peak_malloced_memory":11065664,"does_zap_garbage":0}} +``` + +## Version +[:arrow_heading_up:](#api-overview) + +http://localhost:3000/_version + +``` +{"Xmysql":"0.4.1","mysql":"5.7.15","node":"8.2.1"} +``` ## When to use ? [:arrow_heading_up:](#api-overview) diff --git a/index.js b/index.js index 3c13a04560..9d00bfbe4e 100644 --- a/index.js +++ b/index.js @@ -11,18 +11,18 @@ const dataHelp = require('./lib/util/data.helper.js'); const Xapi = require('./lib/xapi.js'); const cmdargs = require('./lib/util/cmd.helper.js'); -cmdargs.handle(sqlConfig) +cmdargs.handle(sqlConfig); /**************** START : setup express ****************/ let app = express(); -app.use(morgan('tiny')) -app.use(cors()) -app.use(bodyParser.json()) +app.use(morgan('tiny')); +app.use(cors()); +app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true -})) +})); /**************** END : setup express ****************/ @@ -43,16 +43,14 @@ let moreApis = new Xapi(sqlConfig,mysqlPool,app); moreApis.init((err, results) => { - app.listen(sqlConfig.portNumber,sqlConfig.ipAddress) - var t1 = process.hrtime(t) - var t2 = t1[0]+t1[1]/1000000000 + app.listen(sqlConfig.portNumber,sqlConfig.ipAddress); + var t1 = process.hrtime(t); + var t2 = t1[0]+t1[1]/1000000000; console.log(" Xmysql took : %d seconds",dataHelp.round(t2,1)); console.log(' '); console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '); - - -}) +}); /**************** END : setup Xapi ****************/ diff --git a/lib/util/cmd.helper.js b/lib/util/cmd.helper.js index 302eec17df..d70b949e82 100644 --- a/lib/util/cmd.helper.js +++ b/lib/util/cmd.helper.js @@ -1,6 +1,8 @@ 'use strict'; const program = require('commander'); const colors = require('colors'); +const pkginfo = require('pkginfo')(module); + program.on('--help', () => { console.log('') @@ -11,7 +13,7 @@ program.on('--help', () => { }) program - .version('0.4.1') + .version(module.exports.version) .option('-h, --host ', 'hostname of database / localhost by default') .option('-u, --user ', 'username of database / root by default') .option('-p, --password ', 'password of database / empty by default') diff --git a/lib/xapi.js b/lib/xapi.js index 8f5133b067..cdc66cbbd4 100644 --- a/lib/xapi.js +++ b/lib/xapi.js @@ -3,8 +3,11 @@ var Xsql = require('./xsql.js'); var Xctrl = require('./xctrl.js'); var multer = require('multer'); -var path = require('path'); +const path = require('path'); +const pkginfo = require('pkginfo')(module); +const v8 = require('v8'), + os = require('os'); //define class class Xapi { @@ -267,6 +270,12 @@ class Xapi { this.app.get('/download', this.downloadFile.bind(this)); /**************** END : multer routes ****************/ + /**************** START : health and version ****************/ + this.app.get('/_health', this.asyncMiddleware(this.health.bind(this))); + this.app.get('/_version', this.asyncMiddleware(this.version.bind(this))); + /**************** END : health and version ****************/ + + stat.api += 4; } @@ -371,6 +380,50 @@ class Xapi { /**************** END : files related ****************/ + + /**************** START : health and version ****************/ + + async getMysqlUptime() { + let v = await this.mysql.exec('SHOW GLOBAL STATUS LIKE \'Uptime\';', []); + return v[0]['Value']; + } + + async getMysqlHealth() { + let v = await this.mysql.exec('select version() as version', []); + return v[0]['version']; + } + + async health(req, res) { + + let status = {}; + status['process_uptime'] = process.uptime(); + status['mysql_uptime'] = await this.getMysqlUptime(); + + if (Object.keys(req.query).length) { + status['process_memory_usage'] = process.memoryUsage; + status['os_total_memory'] = os.totalmem(); + status['os_free_memory'] = os.freemem(); + status['os_load_average'] = os.loadavg(); + status['v8_heap_statistics'] = v8.getHeapStatistics(); + } + + res.json(status); + + } + + async version(req, res) { + + let version = {}; + + version['Xmysql'] = pkginfo.version; + version['mysql'] = await this.getMysqlHealth(); + version['node'] = process.versions.node; + res.json(version); + + } + + /**************** END : health and version ****************/ + } diff --git a/package-lock.json b/package-lock.json index f85da4c548..cb4301d896 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "xmysql", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -636,6 +636,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pkginfo": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", + "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" + }, "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", diff --git a/package.json b/package.json index 99aa7e17ad..a11b517fdf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xmysql", - "version": "0.4.1", + "version": "0.4.2", "description": "One command to generate REST APIs for any MySql database", "main": "index.js", "scripts": { @@ -34,6 +34,7 @@ "morgan": "^1.9.0", "multer": "^1.3.0", "mysql": "^2.15.0", + "pkginfo": "^0.4.1", "serve-favicon": "^2.4.5" }, "devDependencies": { diff --git a/tests/tests.js b/tests/tests.js index 4f69254df7..ad7c508659 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -8,7 +8,7 @@ var whereClause = require('../lib/util/whereClause.helper.js') var should = require('should'); var request = require('supertest') const cmdargs = require('../lib/util/cmd.helper.js'); - +const pkginfo = require('pkginfo')(module); var args = {} var app = {} @@ -1742,6 +1742,70 @@ describe('xmysql : tests', function () { }); }); + it('/_health should PASS', function (done) { + + //post to an url with data + agent.get('/_health') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + res.body['process_uptime'].should.be.greaterThanOrEqual(0); + res.body['mysql_uptime'].should.be.greaterThanOrEqual(0); + + return done(); + + }); + }); + + it('/_health?details=1 should PASS', function (done) { + + //post to an url with data + agent.get('/_health?details=1') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + res.body['process_uptime'].should.be.greaterThanOrEqual(0); + res.body['mysql_uptime'].should.be.greaterThanOrEqual(0); + res.body['os_total_memory'].should.be.greaterThanOrEqual(0); + + + return done(); + + }); + }); + + it('/_version should PASS', function (done) { + + //post to an url with data + agent.get('/_version') //enter url + .expect(200)//200 for success 4xx for failure + .end(function (err, res) { + + // Handle /api/v error + if (err) { + return done(err); + } + + res.body['Xmysql'].should.equals(pkginfo.version); + res.body['mysql'].should.not.equals(""); + res.body['node'].should.not.equals(""); + + return done(); + + }); + }); + + it('where clause unit ?_where=(abc,eq,1234) should PASS', function (done) { var err = whereClause.getConditionClause('(abc,eq,1234)')