Update customer route and tests
							parent
							
								
									f779ee4baf
								
							
						
					
					
						commit
						a6645e9d81
					
				|  | @ -90,10 +90,22 @@ | ||||||
|             "address1" : "1 test street", |             "address1" : "1 test street", | ||||||
|             "address2" : "testvile", |             "address2" : "testvile", | ||||||
|             "country" : "Netherlands", |             "country" : "Netherlands", | ||||||
|             "state" : "", |             "state" : "NL", | ||||||
|             "postcode" : "2000TW", |             "postcode" : "2000TW", | ||||||
|             "phone" : "123456789", |             "phone" : "123456789", | ||||||
|             "password" : "$2a$10$kKjnX1J/CAdgdmLI0WuPY.ILH1c7N8mD0H/ZyUXEfee1mJxJvZIS." |             "password" : "$2a$10$kKjnX1J/CAdgdmLI0WuPY.ILH1c7N8mD0H/ZyUXEfee1mJxJvZIS." | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "email" : "test1@test.com", | ||||||
|  |             "firstName" : "Testy", | ||||||
|  |             "lastName" : "McTesterson", | ||||||
|  |             "address1" : "2 test street", | ||||||
|  |             "address2" : "testvile", | ||||||
|  |             "country" : "Netherlands", | ||||||
|  |             "state" : "", | ||||||
|  |             "postcode" : "2000ZT", | ||||||
|  |             "phone" : "123456789", | ||||||
|  |             "password" : "$2a$10$kKjnX1J/CAdgdmLI0WuPY.ILH1c7N8mD0H/ZyUXEfee1mJxJvZIS." | ||||||
|         } |         } | ||||||
|     ], |     ], | ||||||
|     "users": [ |     "users": [ | ||||||
|  |  | ||||||
|  | @ -18,6 +18,14 @@ const addSchemas = () => { | ||||||
|     // Amount format
 |     // Amount format
 | ||||||
|     const amountRegex = /^[1-9]\d*(\.\d+)?$/; |     const amountRegex = /^[1-9]\d*(\.\d+)?$/; | ||||||
|     ajv.addFormat('amount', amountRegex); |     ajv.addFormat('amount', amountRegex); | ||||||
|  | 
 | ||||||
|  |     ajv.addKeyword('isNotEmpty', { | ||||||
|  |         type: 'string', | ||||||
|  |         validate: (schema, data) => { | ||||||
|  |           return typeof data === 'string' && data.trim() !== ''; | ||||||
|  |         }, | ||||||
|  |         errors: false | ||||||
|  |     }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const validateJson = (schema, json) => { | const validateJson = (schema, json) => { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| { | { | ||||||
|     "$id": "customer", |     "$id": "editCustomer", | ||||||
|     "type": "object", |     "type": "object", | ||||||
|     "properties": { |     "properties": { | ||||||
|         "email": { |         "email": { | ||||||
|  | @ -7,28 +7,35 @@ | ||||||
|             "format": "emailAddress" |             "format": "emailAddress" | ||||||
|         }, |         }, | ||||||
|         "firstName": { |         "firstName": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "lastName": { |         "lastName": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "address1": { |         "address1": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "address2": { |         "address2": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|         }, |         }, | ||||||
|         "country": { |         "country": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "state": { |         "state": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "postcode": { |         "postcode": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "phone": { |         "phone": { | ||||||
|             "type": "string" |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|         }, |         }, | ||||||
|         "password": { |         "password": { | ||||||
|             "type": "string" |             "type": "string" | ||||||
|  | @ -39,11 +46,9 @@ | ||||||
|         "firstName", |         "firstName", | ||||||
|         "lastName", |         "lastName", | ||||||
|         "address1", |         "address1", | ||||||
|         "address2", |  | ||||||
|         "country", |         "country", | ||||||
|         "state", |         "state", | ||||||
|         "postcode", |         "postcode", | ||||||
|         "phone", |         "phone" | ||||||
|         "password" |  | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  | @ -0,0 +1,56 @@ | ||||||
|  | { | ||||||
|  |     "$id": "newCustomer", | ||||||
|  |     "type": "object", | ||||||
|  |     "properties": { | ||||||
|  |         "email": { | ||||||
|  |             "type": "string", | ||||||
|  |             "format": "emailAddress" | ||||||
|  |         }, | ||||||
|  |         "firstName": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "lastName": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "address1": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "address2": { | ||||||
|  |             "type": "string" | ||||||
|  |         }, | ||||||
|  |         "country": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "state": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "postcode": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "phone": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         }, | ||||||
|  |         "password": { | ||||||
|  |             "type": "string", | ||||||
|  |             "isNotEmpty": true | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "required": [ | ||||||
|  |         "email", | ||||||
|  |         "firstName", | ||||||
|  |         "lastName", | ||||||
|  |         "address1", | ||||||
|  |         "country", | ||||||
|  |         "state", | ||||||
|  |         "postcode", | ||||||
|  |         "phone", | ||||||
|  |         "password" | ||||||
|  |     ] | ||||||
|  | } | ||||||
|  | @ -25,7 +25,7 @@ router.post('/customer/create', async (req, res) => { | ||||||
|         created: new Date() |         created: new Date() | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const schemaResult = validateJson('customer', customerObj); |     const schemaResult = validateJson('newCustomer', customerObj); | ||||||
|     if(!schemaResult.result){ |     if(!schemaResult.result){ | ||||||
|         res.status(400).json(schemaResult.errors); |         res.status(400).json(schemaResult.errors); | ||||||
|         return; |         return; | ||||||
|  | @ -55,6 +55,83 @@ router.post('/customer/create', async (req, res) => { | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | // Update a customer
 | ||||||
|  | router.post('/admin/customer/update', restrict, async (req, res) => { | ||||||
|  |     const db = req.app.db; | ||||||
|  | 
 | ||||||
|  |     const customerObj = { | ||||||
|  |         email: req.body.email, | ||||||
|  |         firstName: req.body.firstName, | ||||||
|  |         lastName: req.body.lastName, | ||||||
|  |         address1: req.body.address1, | ||||||
|  |         address2: req.body.address2, | ||||||
|  |         country: req.body.country, | ||||||
|  |         state: req.body.state, | ||||||
|  |         postcode: req.body.postcode, | ||||||
|  |         phone: req.body.phone | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Handle optional values
 | ||||||
|  |     if(req.body.password){ customerObj.password = bcrypt.hashSync(req.body.password, 10); } | ||||||
|  | 
 | ||||||
|  |     const schemaResult = validateJson('editCustomer', customerObj); | ||||||
|  |     if(!schemaResult.result){ | ||||||
|  |         console.log('errors', schemaResult.errors); | ||||||
|  |         if(req.apiAuthenticated){ | ||||||
|  |             res.status(400).json(schemaResult.errors); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         req.session.message = 'Unable to update customer. Check input values'; | ||||||
|  |         req.session.messageType = 'danger'; | ||||||
|  |         res.redirect('/admin/customer/view/' + req.body.customerId); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // check for existing customer
 | ||||||
|  |     const customer = await db.customers.findOne({ _id: common.getId(req.body.customerId) }); | ||||||
|  |     if(!customer){ | ||||||
|  |         if(req.apiAuthenticated){ | ||||||
|  |             res.status(400).json({ | ||||||
|  |                 err: 'Customer not found' | ||||||
|  |             }); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         req.session.message = 'Customer not found'; | ||||||
|  |         req.session.messageType = 'danger'; | ||||||
|  |         res.redirect('/admin/customer/view/' + req.body.customerId); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     // Update customer
 | ||||||
|  |     try{ | ||||||
|  |         const updatedCustomer = await db.customers.findOneAndUpdate( | ||||||
|  |             { _id: common.getId(req.body.customerId) }, | ||||||
|  |             { | ||||||
|  |                 $set: customerObj | ||||||
|  |             }, { multi: false, returnOriginal: false } | ||||||
|  |         ); | ||||||
|  |         if(req.apiAuthenticated){ | ||||||
|  |             const returnCustomer = updatedCustomer.value; | ||||||
|  |             delete returnCustomer.password; | ||||||
|  |             res.status(200).json({ message: 'Customer updated', customer: updatedCustomer.value }); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // show the view
 | ||||||
|  |         req.session.message = 'Customer updated'; | ||||||
|  |         req.session.messageType = 'success'; | ||||||
|  |         res.redirect('/admin/customer/view/' + req.body.customerId); | ||||||
|  |     }catch(ex){ | ||||||
|  |         console.error(colors.red('Failed updating customer: ' + ex)); | ||||||
|  |         if(req.apiAuthenticated){ | ||||||
|  |             res.status(400).json({ message: 'Failed to update customer' }); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         req.session.message = 'Failed to update customer'; | ||||||
|  |         req.session.messageType = 'danger'; | ||||||
|  |         res.redirect('/admin/customer/view/' + req.body.userId); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| // render the customer view
 | // render the customer view
 | ||||||
| router.get('/admin/customer/view/:id?', restrict, async (req, res) => { | router.get('/admin/customer/view/:id?', restrict, async (req, res) => { | ||||||
|     const db = req.app.db; |     const db = req.app.db; | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ test('[Fail] Try create a duplicate customer', async t => { | ||||||
|     t.deepEqual(res.body.err, 'A customer already exists with that email address'); |     t.deepEqual(res.body.err, 'A customer already exists with that email address'); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('[Fail] Try invalid email address', async t => { | test('[Fail] Create with invalid email address', async t => { | ||||||
|     const customer = { |     const customer = { | ||||||
|         email: 'sarah.jones@test', |         email: 'sarah.jones@test', | ||||||
|         firstName: 'Sarah', |         firstName: 'Sarah', | ||||||
|  | @ -75,6 +75,37 @@ test('[Fail] Try invalid email address', async t => { | ||||||
|     t.deepEqual(res.body[0].message, 'should match format "emailAddress"'); |     t.deepEqual(res.body[0].message, 'should match format "emailAddress"'); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | test('[Success] Update existing customer', async t => { | ||||||
|  |     const customer = { | ||||||
|  |         customerId: g.customers[1]._id, | ||||||
|  |         email: 'sarah.jones@test.com', | ||||||
|  |         firstName: 'Sarah', | ||||||
|  |         lastName: 'Jones', | ||||||
|  |         address1: '1 Sydney Street', | ||||||
|  |         address2: '', | ||||||
|  |         country: 'Australia', | ||||||
|  |         state: 'NSW', | ||||||
|  |         postcode: '2000', | ||||||
|  |         phone: '0444444444' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const res = await g.request | ||||||
|  |         .post('/admin/customer/update') | ||||||
|  |         .send(customer) | ||||||
|  |         .set('apiKey', g.users[0].apiKey) | ||||||
|  |         .expect(200); | ||||||
|  | 
 | ||||||
|  |     t.deepEqual(res.body.message, 'Customer updated'); | ||||||
|  |     t.deepEqual(res.body.customer.email, customer.email); | ||||||
|  |     t.deepEqual(res.body.customer.firstName, customer.firstName); | ||||||
|  |     t.deepEqual(res.body.customer.lastName, customer.lastName); | ||||||
|  |     t.deepEqual(res.body.customer.address1, customer.address1); | ||||||
|  |     t.deepEqual(res.body.customer.country, customer.country); | ||||||
|  |     t.deepEqual(res.body.customer.state, customer.state); | ||||||
|  |     t.deepEqual(res.body.customer.postcode, customer.postcode); | ||||||
|  |     t.deepEqual(res.body.customer.phone, customer.phone); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| test('[Success] Get customer list', async t => { | test('[Success] Get customer list', async t => { | ||||||
|     const res = await g.request |     const res = await g.request | ||||||
|         .get('/admin/customers') |         .get('/admin/customers') | ||||||
|  | @ -82,7 +113,7 @@ test('[Success] Get customer list', async t => { | ||||||
|         .expect(200); |         .expect(200); | ||||||
| 
 | 
 | ||||||
|     // Check the returned customers length
 |     // Check the returned customers length
 | ||||||
|     t.deepEqual(2, res.body.length); |     t.deepEqual(3, res.body.length); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('[Success] Filter customers', async t => { | test('[Success] Filter customers', async t => { | ||||||
|  | @ -92,7 +123,7 @@ test('[Success] Filter customers', async t => { | ||||||
|         .expect(200); |         .expect(200); | ||||||
| 
 | 
 | ||||||
|     // Check the returned customers length
 |     // Check the returned customers length
 | ||||||
|     t.deepEqual(2, res.body.length); |     t.deepEqual(3, res.body.length); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('[Success] Get single customer', async t => { | test('[Success] Get single customer', async t => { | ||||||
|  | @ -109,7 +140,7 @@ test('[Fail] Customer login with incorrect email', async t => { | ||||||
|     const res = await g.request |     const res = await g.request | ||||||
|         .post('/customer/login_action') |         .post('/customer/login_action') | ||||||
|         .send({ |         .send({ | ||||||
|             loginEmail: 'test1@test.com', |             loginEmail: 'test1111@test.com', | ||||||
|             loginPassword: 'test' |             loginPassword: 'test' | ||||||
|         }) |         }) | ||||||
|         .expect(400); |         .expect(400); | ||||||
|  |  | ||||||
|  | @ -1,49 +1,84 @@ | ||||||
| {{> partials/menu}} | {{> partials/menu}} | ||||||
| <div class="col-lg-9"> | <div class="col-lg-9"> | ||||||
|     <div class="row"> |     <div class="row"> | ||||||
|         <div class="col-lg-12"> |         <form method="post" class="form-horizontal" id="update_form" action="/admin/customer/update" data-toggle="validator"> | ||||||
|  |             <div class="col-xs-12 col-md-12"> | ||||||
|  |                 <div class="page-header"> | ||||||
|  |                     <div class="pull-right"> | ||||||
|  |                         <button id="frm_edit_product_save" class="btn btn-success">Save customer <i class="fa fa-floppy-o"></i></button> | ||||||
|  |                     </div> | ||||||
|                     <h2>Customer</h2> |                     <h2>Customer</h2> | ||||||
|                 </div> |                 </div> | ||||||
|         <div class="col-xs-12 col-md-12"> |                 <div class="form-group"> | ||||||
|             <table class="table-bordered"> |                     <label class="col-sm-2 control-label">Email *</label> | ||||||
|                 <tr> |                     <div class="col-sm-10"> | ||||||
|                     <td><span class="text-info">Email:</span></td> |                         <input type="text" class="form-control" name="email" value={{result.email}} required> | ||||||
|                     <td>{{result.email}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Name" }}:</span></td> |  | ||||||
|                     <td>{{result.firstName}} {{result.lastName}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Address 1" }}:</span></td> |  | ||||||
|                     <td>{{result.address1}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Address 2" }}:</span></td> |  | ||||||
|                     <td>{{result.address2}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Country" }}:</span></td> |  | ||||||
|                     <td>{{result.country}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "State" }}:</span></td> |  | ||||||
|                     <td>{{result.state}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Postcode" }}:</span></td> |  | ||||||
|                     <td>{{result.postcode}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Phone number" }}:</span></td> |  | ||||||
|                     <td>{{result.phone}}</td> |  | ||||||
|                 </tr> |  | ||||||
|                 <tr> |  | ||||||
|                     <td><span class="text-info">{{ @root.__ "Creation date" }}:</span></td> |  | ||||||
|                     <td>{{formatDate result.created "DD/MM/YYYY hh:mmA"}}</td> |  | ||||||
|                 </tr> |  | ||||||
|             </table> |  | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|     <input type="hidden" id="productId" value="{{result._id}}"> |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "First name" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="firstName" value={{result.firstName}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Last name" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="lastName" value={{result.lastName}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Address 1" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="address1" value={{result.address1}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Address 2" }}</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="address2" value={{result.address2}}> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Country" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="country" value={{result.country}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "State" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="state" value={{result.state}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Postcode" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="postcode" value={{result.postcode}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Phone number" }} *</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" name="phone" value={{result.phone}} required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Password" }}</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="password" class="form-control" name="password"> | ||||||
|  |                         <p class="help-block">Only populate if wanting to reset the customers password</p> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="form-group"> | ||||||
|  |                     <label class="col-sm-2 control-label">{{ @root.__ "Creation date" }}</label> | ||||||
|  |                     <div class="col-sm-10"> | ||||||
|  |                         <input type="text" class="form-control" value="{{formatDate result.created "DD/MM/YYYY hh:mmA"}}" readonly> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <input type="hidden" name="customerId" value="{{result._id}}"> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
| </div> | </div> | ||||||
		Loading…
	
		Reference in New Issue