Fixed validation, added modal errors + partial
parent
6806806fa2
commit
9911fe04a9
|
@ -3,7 +3,7 @@ const fs = require('fs');
|
|||
const moment = require('moment');
|
||||
const glob = require('glob');
|
||||
const Ajv = require('ajv');
|
||||
const ajv = new Ajv();
|
||||
const ajv = new Ajv({ allErrors: true, jsonPointers: true });
|
||||
|
||||
const addSchemas = () => {
|
||||
const schemaFiles = glob.sync('./lib/**/*.json');
|
||||
|
@ -28,12 +28,18 @@ const addSchemas = () => {
|
|||
});
|
||||
|
||||
ajv.addKeyword('isNotEmpty', {
|
||||
type: 'string',
|
||||
validate: (schema, data) => {
|
||||
return typeof data === 'string' && data.trim() !== '';
|
||||
validate: function validate(schema, data){
|
||||
const result = typeof data === 'string' && data.trim() !== '';
|
||||
if(!result){
|
||||
console.log('result', result);
|
||||
validate.errors = [{ keyword: 'isNotEmpty', message: 'Cannot be an empty string', params: { keyword: 'isNotEmpty' } }];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
errors: false
|
||||
errors: true
|
||||
});
|
||||
|
||||
require('ajv-errors')(ajv);
|
||||
};
|
||||
|
||||
const validateJson = (schema, json) => {
|
||||
|
|
|
@ -6,17 +6,23 @@
|
|||
"type": "string"
|
||||
},
|
||||
"productPermalink": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 2
|
||||
},
|
||||
"productTitle": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 5
|
||||
},
|
||||
"productPrice": {
|
||||
"type": "string",
|
||||
"format": "amount"
|
||||
},
|
||||
"productDescription": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 25
|
||||
},
|
||||
"productPublished": {
|
||||
"type": "boolean"
|
||||
|
@ -34,6 +40,14 @@
|
|||
"type": ["number", "null"]
|
||||
}
|
||||
},
|
||||
"errorMessage": {
|
||||
"isNotEmpty": "This is my custom error message",
|
||||
"properties": {
|
||||
"productPrice": "Should be a full 2 decimal value. Eg: 10.99",
|
||||
"productPublished": "Should be either true or false",
|
||||
"productComment": "Should be either true or false"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"productId"
|
||||
]
|
||||
|
|
|
@ -3,17 +3,23 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"productPermalink": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 2
|
||||
},
|
||||
"productTitle": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 5
|
||||
},
|
||||
"productPrice": {
|
||||
"type": "string",
|
||||
"format": "amount"
|
||||
},
|
||||
"productDescription": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"isNotEmpty": true,
|
||||
"minLength": 25
|
||||
},
|
||||
"productPublished": {
|
||||
"type": "boolean"
|
||||
|
@ -31,6 +37,13 @@
|
|||
"type": ["number", "null"]
|
||||
}
|
||||
},
|
||||
"errorMessage": {
|
||||
"properties": {
|
||||
"productPrice": "Should be a full 2 decimal value. Eg: 10.99",
|
||||
"productPublished": "Should be either true or false",
|
||||
"productComment": "Should be either true or false"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"productPermalink",
|
||||
"productTitle",
|
||||
|
|
|
@ -610,6 +610,11 @@
|
|||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-errors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
|
||||
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ=="
|
||||
},
|
||||
"align-text": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
|
||||
|
@ -1377,7 +1382,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -4046,7 +4052,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -10137,7 +10144,8 @@
|
|||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"dependencies": {
|
||||
"@adyen/api-library": "^2.1.7",
|
||||
"ajv": "^6.10.2",
|
||||
"ajv-errors": "^1.0.1",
|
||||
"async": "^2.6.3",
|
||||
"axios": "^0.19.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
|
|
@ -218,6 +218,12 @@ $(document).ready(function (){
|
|||
showNotification(msg.message, 'success', false, '/admin/product/edit/' + msg.productId);
|
||||
})
|
||||
.fail(function(msg){
|
||||
if(msg.responseJSON.length > 0){
|
||||
var errorMessages = validationErrors(msg.responseJSON);
|
||||
$('#validationModalBody').html(errorMessages);
|
||||
$('#validationModal').modal('show');
|
||||
return;
|
||||
}
|
||||
showNotification(msg.responseJSON.message, 'danger');
|
||||
});
|
||||
}
|
||||
|
@ -250,6 +256,13 @@ $(document).ready(function (){
|
|||
showNotification(msg.message, 'success', true);
|
||||
})
|
||||
.fail(function(msg){
|
||||
if(msg.responseJSON.length > 0){
|
||||
var errorMessages = validationErrors(msg.responseJSON);
|
||||
console.log('errorMessages', errorMessages);
|
||||
$('#validationModalBody').html(errorMessages);
|
||||
$('#validationModal').modal('show');
|
||||
return;
|
||||
}
|
||||
showNotification(msg.responseJSON.message, 'danger');
|
||||
});
|
||||
}
|
||||
|
@ -842,3 +855,11 @@ function globalSearch(){
|
|||
feather.replace();
|
||||
});
|
||||
}
|
||||
|
||||
function validationErrors(errors){
|
||||
var errorMessage = '';
|
||||
errors.forEach((value) => {
|
||||
errorMessage += `<p>${value.dataPath.replace('/', '')} - <span class="text-danger">${value.message}<span></p>`;
|
||||
});
|
||||
return errorMessage;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -249,10 +249,7 @@ router.post('/admin/product/update', restrict, checkAccess, async (req, res) =>
|
|||
// Validate the body again schema
|
||||
const schemaValidate = validateJson('editProduct', productDoc);
|
||||
if(!schemaValidate.result){
|
||||
res.status(400).json({
|
||||
message: 'Form invalid. Please check values and try again.',
|
||||
error: schemaValidate.errors
|
||||
});
|
||||
res.status(400).json(schemaValidate.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ test('[Success] Add a product', async t => {
|
|||
productPermalink: 'test-jacket',
|
||||
productTitle: 'Test Jacket',
|
||||
productPrice: '100.00',
|
||||
productDescription: 'Test desc',
|
||||
productDescription: 'Test product description used to describe the product',
|
||||
productPublished: true,
|
||||
productTags: 'organic, jacket',
|
||||
productOptions: {
|
||||
|
@ -94,7 +94,7 @@ test('[Fail] Add a product - Duplicate permalink', async t => {
|
|||
productPermalink: 'test-jacket',
|
||||
productTitle: 'Test Jacket - blue',
|
||||
productPrice: '100.00',
|
||||
productDescription: 'Test desc blue',
|
||||
productDescription: 'Test product description used to describe the product',
|
||||
productPublished: true,
|
||||
productTags: 'organic, jacket, blue',
|
||||
productOptions: {
|
||||
|
@ -124,7 +124,7 @@ test('[Success] Update a product', async t => {
|
|||
productId: g.products[0]._id,
|
||||
productTitle: 'Test Jacket',
|
||||
productPrice: '200.00',
|
||||
productDescription: 'Test desc',
|
||||
productDescription: 'Test product description used to describe the product',
|
||||
productPublished: true,
|
||||
productTags: 'organic, jacket',
|
||||
productOptions: {
|
||||
|
|
|
@ -259,27 +259,8 @@
|
|||
{{/if}}
|
||||
<script src="/javascripts/pushy.min.js"></script>
|
||||
{{#if admin}}
|
||||
<div class="modal fade" id="globalSearchModal" tabindex="-1" role="dialog" aria-labelledby="globalSearchModal"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content global-search-modal-content">
|
||||
<div class="modal-header global-search-modal-header"></div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group global-search-form">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text search-input-addon" for="global-search-value">{{{feather 'search'}}}</label>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-lg" id="global-search-value">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="list-group col-12 invisible" id="global-search-results"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> partials/globalSearchModal}}
|
||||
{{> partials/validationModal}}
|
||||
{{/if}}
|
||||
{{> partials/confirmModal}}
|
||||
<script>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<div class="modal fade" id="globalSearchModal" tabindex="-1" role="dialog" aria-labelledby="globalSearchModal" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content global-search-modal-content">
|
||||
<div class="modal-header global-search-modal-header"></div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group global-search-form">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text search-input-addon" for="global-search-value">{{{feather 'search'}}}</label>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-lg" id="global-search-value">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="list-group col-12 invisible" id="global-search-results"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
<div class="modal fade" id="validationModal" tabindex="-1" role="dialog" aria-labelledby="validationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title text-danger" id="validationModalLabel">Error</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="validationModalBody"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary mr-auto" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue