Permalink
Browse files

extract 'CustomersImporter' in to it's own module

  • Loading branch information...
andriykuba committed Jan 10, 2014
1 parent 3dc0729 commit 4ad2c8b3d33f22985746ef6322ba70155d45f7c2
Showing with 329 additions and 277 deletions.
  1. +42 −0 log/server.log
  2. +285 −0 modules/aku-debitoor.js
  3. +2 −277 routes/debitoor.js
View
@@ -907,3 +907,45 @@
{"level":"info","message":"ignored: douglas.crockford@gmail.com","timestamp":"2014-01-10T16:38:10.679Z"}
{"level":"info","message":"ignored: nicholas@czakas.com","timestamp":"2014-01-10T16:38:10.679Z"}
{"level":"info","message":"send: dean@edwards.com(Dean Edwards)","timestamp":"2014-01-10T16:38:11.163Z"}
+{"level":"info","message":"Environment: test","timestamp":"2014-01-10T23:09:28.634Z"}
+{"level":"info","message":"Express server run on: http://127.0.0.1:3000","timestamp":"2014-01-10T23:09:28.727Z"}
+{"level":"info","message":"Mongoose default connection disconnected","timestamp":"2014-01-10T23:09:29.728Z"}
+{"level":"info","message":"Mongoose default connection error: Error: failed to connect to [localhost:27017]","timestamp":"2014-01-10T23:09:29.729Z"}
+{"level":"info","message":"Environment: test","timestamp":"2014-01-10T23:09:59.712Z"}
+{"level":"info","message":"Express server run on: http://127.0.0.1:3000","timestamp":"2014-01-10T23:09:59.766Z"}
+{"level":"info","message":"Test database has been dropped","timestamp":"2014-01-10T23:09:59.792Z"}
+{"level":"info","message":"File has been uploaded","timestamp":"2014-01-10T23:10:00.787Z"}
+{"level":"info","message":"Number of contacts: 4","timestamp":"2014-01-10T23:10:00.814Z"}
+{"level":"info","message":"Schememap has been saved","timestamp":"2014-01-10T23:10:01.010Z"}
+{"level":"info","message":"User has been created: user1","timestamp":"2014-01-10T23:10:03.711Z"}
+{"level":"info","message":"Debitoor token has been saved","timestamp":"2014-01-10T23:10:04.204Z"}
+{"level":"info","message":"Updated: jhon@resing.com","timestamp":"2014-01-10T23:10:05.400Z"}
+{"level":"info","message":"Updated: dean@edwards.com","timestamp":"2014-01-10T23:10:05.426Z"}
+{"level":"info","message":"Updated: nicholas@czakas.com","timestamp":"2014-01-10T23:10:05.470Z"}
+{"level":"info","message":"Updated: douglas.crockford@gmail.com","timestamp":"2014-01-10T23:10:05.483Z"}
+{"level":"error","message":"TypeError: undefined is not a function\n at customersImport (D:\\Dropbox\\dits\\api\\routes\\debitoor.js:67:17)\n at callbacks (D:\\Dropbox\\dits\\api\\node_modules\\express\\lib\\router\\index.js:164:37)\n at Promise.<anonymous> (D:\\Dropbox\\dits\\api\\routes\\user.js:31:11)\n at Promise.<anonymous> (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\node_modules\\mpromise\\lib\\promise.js:171:8)\n at Promise.EventEmitter.emit (events.js:95:17)\n at Promise.emit (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\node_modules\\mpromise\\lib\\promise.js:88:38)\n at Promise.fulfill (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\node_modules\\mpromise\\lib\\promise.js:101:20)\n at D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\lib\\query.js:1412:13\n at model.Document.init (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\lib\\document.js:250:11)\n at completeOne (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\lib\\query.js:1410:10)\n at Object.cb (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\lib\\query.js:1169:11)\n at Object._onImmediate (D:\\Dropbox\\dits\\api\\node_modules\\mongoose\\node_modules\\mquery\\lib\\utils.js:126:16)\n at processImmediate [as _immediateCallback] (timers.js:330:15)","timestamp":"2014-01-10T23:10:05.497Z"}
+{"level":"info","message":"Environment: test","timestamp":"2014-01-10T23:11:02.071Z"}
+{"level":"info","message":"Express server run on: http://127.0.0.1:3000","timestamp":"2014-01-10T23:11:02.133Z"}
+{"level":"info","message":"Mongoose default connection open to mongodb://localhost/debitoorimporter-test","timestamp":"2014-01-10T23:11:02.148Z"}
+{"level":"info","message":"All Components ready to action.","timestamp":"2014-01-10T23:11:02.149Z"}
+{"level":"info","message":"Test database has been dropped","timestamp":"2014-01-10T23:11:02.163Z"}
+{"level":"info","message":"File has been uploaded","timestamp":"2014-01-10T23:11:02.756Z"}
+{"level":"info","message":"Number of contacts: 4","timestamp":"2014-01-10T23:11:02.784Z"}
+{"level":"info","message":"Schememap has been saved","timestamp":"2014-01-10T23:11:02.952Z"}
+{"level":"info","message":"User has been created: user1","timestamp":"2014-01-10T23:11:05.014Z"}
+{"level":"info","message":"Debitoor token has been saved","timestamp":"2014-01-10T23:11:05.505Z"}
+{"level":"info","message":"send: douglas.crockford@gmail.com(ENTER NAME PLEASE)","timestamp":"2014-01-10T23:11:06.511Z"}
+{"level":"info","message":"send: jhon@resing.com(Jhon Resing)","timestamp":"2014-01-10T23:11:06.524Z"}
+{"level":"info","message":"send: dean@edwards.com(Dean Edwards)","timestamp":"2014-01-10T23:11:06.550Z"}
+{"level":"info","message":"send: nicholas@czakas.com(Nicholas C. Zakas)","timestamp":"2014-01-10T23:11:06.575Z"}
+{"level":"info","message":"Updated: douglas.crockford@gmail.com","timestamp":"2014-01-10T23:11:08.211Z"}
+{"level":"info","message":"Updated: dean@edwards.com","timestamp":"2014-01-10T23:11:08.241Z"}
+{"level":"info","message":"to update: douglas.crockford@gmail.com","timestamp":"2014-01-10T23:11:08.930Z"}
+{"level":"info","message":"send: dean@edwards.com(Dean Edwards)","timestamp":"2014-01-10T23:11:09.423Z"}
+{"level":"info","message":"send: douglas.crockford@gmail.com(ENTER NAME PLEASE)","timestamp":"2014-01-10T23:11:09.436Z"}
+{"level":"info","message":"Updated: dean@edwards.com","timestamp":"2014-01-10T23:11:11.019Z"}
+{"level":"info","message":"Updated: douglas.crockford@gmail.com","timestamp":"2014-01-10T23:11:11.037Z"}
+{"level":"info","message":"ignored: jhon@resing.com","timestamp":"2014-01-10T23:11:11.721Z"}
+{"level":"info","message":"ignored: douglas.crockford@gmail.com","timestamp":"2014-01-10T23:11:11.722Z"}
+{"level":"info","message":"ignored: nicholas@czakas.com","timestamp":"2014-01-10T23:11:11.722Z"}
+{"level":"info","message":"send: dean@edwards.com(Dean Edwards)","timestamp":"2014-01-10T23:11:12.284Z"}
View
@@ -0,0 +1,285 @@
+/* jslint node: true */
+'use strict';
+
+var log = require('winston');
+var request = require('request');
+var async = require('async');
+
+var config = require('../modules/aku-config');
+var db = require('../modules/aku-database');
+var akuString = require('../modules/aku-string');
+
+var CustomersImporter = function () {
+ this.token = null;
+ this.mergeRule = null;
+};
+
+CustomersImporter.prototype.mapContact = function(schememap, contact){
+ function mapContact(field){
+ return contact[field];
+ }
+
+ var customer = {};
+ for (var x in schememap) {
+ var item = schememap[x];
+ var isMappable = typeof item.map !== 'undefined';
+ var isObligatory = typeof item.default !== 'undefined';
+
+ var value = null;
+ if(isMappable){
+ var isDelimeterPresent = typeof item.delimeter !== 'undefined';
+ var delimeter = isDelimeterPresent ? item.delimeter : ',';
+
+
+ value = item.map.map(mapContact).join(delimeter);
+ value = akuString.fulltrim(value);
+ if(value === ''){
+ value = null;
+ }
+ }
+
+ if(value === null && isObligatory){
+ value = item.default;
+ }
+
+ if(value !== null){
+ customer[x] = value;
+ }
+ }
+ return customer;
+};
+
+CustomersImporter.prototype.detectMergeRule = function(req){
+ //we assume that email is contact id. so "update" and "ignore" merge
+ //using the contact\customer email field for authentication
+ var mergeRules = ['add', 'update', 'ignore'];
+ var mergeRuleDefault = mergeRules[0];
+
+ var body = req.body || {};
+ var mergeRule = body.mergeRule || mergeRuleDefault;
+
+ if(mergeRules.indexOf(mergeRule)<0){
+ mergeRule = mergeRuleDefault;
+ }
+
+ return mergeRule;
+};
+
+CustomersImporter.prototype.customerUpdate = function(customer, id, cb){
+ var url = config.debitoor.api.customersURL+'/'+id;
+ this.customerSend(customer, url, 'PUT', cb);
+};
+
+CustomersImporter.prototype.customerCreate = function(customer, cb){
+ var url = config.debitoor.api.customersURL+'?autonumber=true';
+ this.customerSend(customer, url, 'POST', cb);
+};
+
+CustomersImporter.prototype.customerSend = function(customer, url, method, cb){
+ request({
+ url:url,
+ json:customer,
+ method: method,
+ headers:{
+ 'x-token': this.token
+ }
+ },function (err, response, body) {
+ if(err){
+ cb(err);
+ return;
+ }
+
+ if(response.statusCode != 200){
+ cb('Debitoor respone code on '+customer.email+':'+response.statusCode);
+ return;
+ }
+
+ log.info('send: ' + customer.email + '('+customer.name+')');
+ cb(null, body);
+ });
+};
+
+CustomersImporter.prototype.customerCreateTask = function(){
+ var self = this;
+ return function(contact){
+ return function(callback){
+ self.customerCreate(contact, callback);
+ };
+ };
+};
+
+CustomersImporter.prototype.createCustomerEmailMapper = function(customers){
+ var customersMap = {};
+ for (var i = 0; customers.length > i; i += 1) {
+ if(typeof customers[i].email !== 'undefined'){
+ customersMap[customers[i].email] = customers[i];
+ }
+ }
+
+ return function(email) {
+ return customersMap[email];
+ };
+};
+
+CustomersImporter.prototype.createTasksMergeUpdate = function(data, contacts){
+ var self = this;
+ var customerMapper = this.createCustomerEmailMapper(data.customers);
+
+ var update = [];
+ var tasks = contacts
+ .filter(function(contact){
+ if(!contact.email) return true;
+ var customer = customerMapper(contact.email);
+
+ var isPresent = typeof customer !== 'undefined';
+ if(isPresent){
+ var isDifferent = false;
+ for(var x in contact){
+ if(customer[x] !== contact[x]){
+ customer[x] = contact[x];
+ isDifferent = true;
+ }
+ }
+
+ if(isDifferent){
+ log.info('to update: '+contact.email);
+ update.push(function (callback){
+ self.customerUpdate(customer, customer.id, callback);
+ });
+ }
+ }
+
+ return !isPresent;
+ })
+ .map(this.customerCreateTask());
+ return tasks.concat(update);
+};
+
+CustomersImporter.prototype.createTasksMergeIgnore = function(data, contacts){
+ var customerMapper = this.createCustomerEmailMapper(data.customers);
+
+ var tasks = contacts
+ .filter(function(contact){
+ if(!contact.email) return true;
+ var customer = customerMapper(contact.email);
+
+ var isPresent = typeof customer !== 'undefined';
+ if(isPresent){
+ log.info('ignored: '+contact.email);
+ }
+
+ return !isPresent;
+ })
+ .map(this.customerCreateTask());
+
+ return tasks;
+};
+
+CustomersImporter.prototype.importContacts = function(data, cb){
+ var self = this;
+ var contacts = data.contacts.map(function(contact){
+ return self.mapContact(data.schememap, contact);
+ });
+
+ var tasks = [];
+ switch(this.mergeRule){
+ case 'add':
+ tasks = contacts.map(this.customerCreateTask());
+ break;
+ case 'update':
+ tasks = this.createTasksMergeUpdate(data, contacts);
+ break;
+ case 'ignore':
+ tasks = this.createTasksMergeIgnore(data, contacts);
+ break;
+ default:
+ cb('No such merge Rule:' + this.mergeRule);
+ }
+
+ async.parallel(
+ tasks,
+ function(err){
+ if(err){
+ cb(err);
+ return;
+ }
+ cb();
+ });
+};
+
+CustomersImporter.prototype.readCustomers = function(req, cb){
+ var url = config.debitoor.api.customersURL;
+
+ request({
+ url:url,
+ json:true,
+ headers:{
+ 'x-token': this.token
+ }
+ },function (err, response, body) {
+ if(err){
+ cb(err);
+ return;
+ }
+
+ if(response.statusCode != 200){
+ cb('Debitoor respone code: ' + response.statusCode);
+ return;
+ }
+
+ //"isArchived" means not available for user
+ //so we need to filter this customers
+ var customers = body.filter(function(customer){
+ return !customer.isArchived;
+ });
+
+ cb(null, customers);
+ });
+};
+
+CustomersImporter.prototype.process = function(req, res, next){
+ var self = this;
+
+ this.token = req.userdata.debitoortoken;
+ this.mergeRule = this.detectMergeRule(req);
+
+ var tasks = {
+ schememap: function(cb){
+ db.readSchememap(req.user, cb);
+ },
+ contacts: function(cb){
+ db.readContacts(req.user, cb);
+ }
+ };
+
+ if(this.mergeRule !== 'add'){
+ this.customerIdUrlPart = config.debitoor.api.customersURL + '/';
+ this.tokenUrlPart = '?token=' + req.userdata.debitoortoken;
+
+ tasks.customers = function(cb){
+ self.readCustomers(req, cb);
+ };
+ }
+
+ async.parallel(
+ tasks,
+ function(err, data){
+ if(err){
+ next(err);
+ return;
+ }
+
+ self.importContacts(data, function(err){
+ if(err){
+ next(err);
+ return;
+ }
+
+ res.send({
+ 'complete': true
+ });
+ });
+ });
+};
+
+exports.CustomersImporter = CustomersImporter;
Oops, something went wrong.

0 comments on commit 4ad2c8b

Please sign in to comment.