1 /**
  2  * @author  Brian Carlsen
  3  * @version  1.0.0
  4  *
  5  * A logger for tracking calls to the MINDBODY API
  6  */
  7 
  8 var fs 		= require( 'fs' ),
  9 	http 	= require( 'http' );
 10 
 11 
 12 /**
 13  * Used to log requests to the MINDBODY server.
 14  *
 15  * @param {string} type The type of logger to be used. 
 16  *		Either 'local' to log to a local file, or
 17  * 		'remote' to log to a remote server. 
 18  *
 19  * @throws {Error} Throws error if an invalid type parameter is passed.
 20  */
 21  function mbo_Logger( type ) {
 22  	if ( [ 'local', 'remote' ].indexOf( type ) === -1 ) {
 23  		// invalid type
 24  		throw new Error( 'Invalid type for mbo_Logger. Must be "local" or "remote".' );
 25  	}
 26  	
 27  	this.type = type;
 28  	this.host = undefined;
 29  	this.port = 80;
 30  	this.path = undefined;
 31  }
 32 
 33 /**
 34  * Sets the host and port of the logger. Can only be used for remote loggers."
 35  *
 36  * @param {string} host The host for the Logger to post data to.
 37  * @param {int} port The port to acces the host by.
 38  *
 39  * @throws {Error} If Logger type is not remote.
 40  */ 
 41 mbo_Logger.prototype.setHost = function( host, port = 80 ) {
 42 	if ( this.type !== 'remote' ) {
 43 		throw new Error( 'mbo_Logger must be of type remote to set host.' );
 44 	}
 45 
 46 	this.host = host;
 47 	this.port = port;
 48 };
 49 
 50 /**
 51  * Sets the path to send the log to.
 52  * If a local Logger, path should point to a file to append to.
 53  * If a remote Logger, the path of the host to post the data to.
 54  *
 55  * @param {string} path The path for the Logger to post to.
 56  */
 57 mbo_Logger.prototype.setPath = function( path ) {
 58 	this.path = path;
 59 };
 60 
 61 /**
 62  * Creates data string to be logged.
 63  *
 64  * @param {object} params The parameters submitted during the call.
 65  * @param {string} method The method called.
 66  * @param {object} result The result of the call.
 67  *
 68  * @return {string} Returns the JSON stringified version of the request.
 69  */
 70 mbo_Logger.prototype._createLoggerData = function( service, params, method, result ) {
 71 	var loggerData = {
 72 		service: 	service,
 73 		params: 	params,
 74 		method: 	method,
 75 		error: 		undefined
 76 	};
 77 
 78 	var res = result[ method + 'Result' ];
 79 	if ( res.ErrorCode !== 200 ) { // SOAP Fault occurred
 80 		loggerData.error = {
 81 			status: 	res.Status,
 82 			errorCode: 	res.ErrorCode,
 83 			message: 	res.Message
 84 		};
 85 	}
 86 
 87 	return JSON.stringify( loggerData ); 
 88 };
 89 
 90 /**
 91  * Internal function used for local logging.
 92  * Writes log data to a local file.
 93  * path defaults to ./mbo-api.log
 94  *
 95  * @param {object} params The request parameters submitted to the MINDBODY API.
 96  * @param {string} method The name of the method called on the MINDBODY API.
 97  * @param {object} result The result from the function call.
 98  */
 99 mbo_Logger.prototype._logLocal = function( params, method, result ) {
100 	var data = this._createLoggerData( params, method, result );
101 	data = '[' + ( new Date() ).toISOString() + '] ' + data + '\n';
102 
103 	var path = this.path || './mbo-api.log';
104 	fs.appendFile( path, data, function( err ) {
105 		if ( err ) {
106 			throw err;
107 		}
108 	} );
109 };
110 
111 /**
112  * Internal function used for remote logging.
113  * Posts data to the set host, port, and path.
114  * host defaults to localhost, port defaults to 80, and path defaults to /.
115  *
116  * @param {object} params The request parameters submitted to the MINDBODY API.
117  * @param {string} method The name of the method called on the MINDBODY API.
118  * @param {object} result The result from the function call.
119  */
120 mbo_Logger.prototype._logRemote = function( params, method, result ) {
121 	// check host and path are set
122 
123 	var loggerParams = {
124 		host: 	this.host || 'localhost',
125 		port: 	this.port || 80,
126 		path: 	this.path || '/',
127 		method: 'post',
128 		headers: {
129 			'Content-Type': 	'application/json',
130 			'Content-Length': 	undefined
131 		}
132 	};
133 
134 	var loggerData = this._createLoggerData( params, method, result)
135 	loggerParams.headers['Content-Length'] = loggerData.length;
136 
137 	var loggerReq = http.request( loggerParams, function( res ) {
138 		var data = '';
139 		res.setEncoding( 'utf8' );
140 
141 		res.on( 'data', function( chunk ) {
142 			data += chunk;
143 		} );
144 
145 		res.on( 'end', function() {
146 			if ( res.statusCode !== 200 ) {
147 				console.log( "[MBO Logger Error]", data, loggerData.error );
148 			}
149 		} );
150 	} )
151 	.on( 'error', function( err ) {
152 		console.log( "[MBO Logger]", err );
153 	} );
154 
155 	loggerReq.write( loggerData );
156 	loggerReq.end();
157 };
158 
159 mbo_Logger.prototype.log = function( params, method, result ) {
160 	switch ( this.type ) {
161 		case 'local':
162 			this._logLocal( params, method, result );
163 			break;
164 		case 'remote':
165 			this._logRemote( params, method, result );
166 			break;
167 		default:
168 			throw new Error( 'Invalid type property' );
169 			break;
170 	};
171 };
172 
173 module.exports = mbo_Logger;