Update customer route and tests

master
Mark Moffat 2019-11-16 21:27:48 +10:30
parent f779ee4baf
commit a6645e9d81
7 changed files with 285 additions and 61 deletions

View File

@ -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": [

View File

@ -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) => {

View File

@ -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"
] ]
} }

View File

@ -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"
]
}

View File

@ -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;

View File

@ -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);

View File

@ -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">
<h2>Customer</h2> <div class="col-xs-12 col-md-12">
</div> <div class="page-header">
<div class="col-xs-12 col-md-12"> <div class="pull-right">
<table class="table-bordered"> <button id="frm_edit_product_save" class="btn btn-success">Save customer <i class="fa fa-floppy-o"></i></button>
<tr> </div>
<td><span class="text-info">Email:</span></td> <h2>Customer</h2>
<td>{{result.email}}</td> </div>
</tr> <div class="form-group">
<tr> <label class="col-sm-2 control-label">Email *</label>
<td><span class="text-info">{{ @root.__ "Name" }}:</span></td> <div class="col-sm-10">
<td>{{result.firstName}} {{result.lastName}}</td> <input type="text" class="form-control" name="email" value={{result.email}} required>
</tr> </div>
<tr> </div>
<td><span class="text-info">{{ @root.__ "Address 1" }}:</span></td> <div class="form-group">
<td>{{result.address1}}</td> <label class="col-sm-2 control-label">{{ @root.__ "First name" }} *</label>
</tr> <div class="col-sm-10">
<tr> <input type="text" class="form-control" name="firstName" value={{result.firstName}} required>
<td><span class="text-info">{{ @root.__ "Address 2" }}:</span></td> </div>
<td>{{result.address2}}</td> </div>
</tr> <div class="form-group">
<tr> <label class="col-sm-2 control-label">{{ @root.__ "Last name" }} *</label>
<td><span class="text-info">{{ @root.__ "Country" }}:</span></td> <div class="col-sm-10">
<td>{{result.country}}</td> <input type="text" class="form-control" name="lastName" value={{result.lastName}} required>
</tr> </div>
<tr> </div>
<td><span class="text-info">{{ @root.__ "State" }}:</span></td> <div class="form-group">
<td>{{result.state}}</td> <label class="col-sm-2 control-label">{{ @root.__ "Address 1" }} *</label>
</tr> <div class="col-sm-10">
<tr> <input type="text" class="form-control" name="address1" value={{result.address1}} required>
<td><span class="text-info">{{ @root.__ "Postcode" }}:</span></td> </div>
<td>{{result.postcode}}</td> </div>
</tr> <div class="form-group">
<tr> <label class="col-sm-2 control-label">{{ @root.__ "Address 2" }}</label>
<td><span class="text-info">{{ @root.__ "Phone number" }}:</span></td> <div class="col-sm-10">
<td>{{result.phone}}</td> <input type="text" class="form-control" name="address2" value={{result.address2}}>
</tr> </div>
<tr> </div>
<td><span class="text-info">{{ @root.__ "Creation date" }}:</span></td> <div class="form-group">
<td>{{formatDate result.created "DD/MM/YYYY hh:mmA"}}</td> <label class="col-sm-2 control-label">{{ @root.__ "Country" }} *</label>
</tr> <div class="col-sm-10">
</table> <input type="text" class="form-control" name="country" value={{result.country}} required>
</div> </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>
<input type="hidden" id="productId" value="{{result._id}}">
</div> </div>