Validate the schema - node.js

How can we validate the written schema valid or not .
const schema = {
"properties": {
"foo": { "add": "string" , "minLenfeffgth": 3, "maxLefngth": 255 }
}
};
above mention schema are valid schema according to ajv.validateSchema() .
like we validating the data , there is any function who validate the schema .
complete code :
var Ajv = require('ajv');
var ajv = new Ajv({ allErrors: true});
const schema = {
"properties": {
"foo": { "add": "string" , "minLenfeffgth": 3, "maxLefngth": 255 }
}
};
// console.log(ajv.validateSchema(schema));
var validate = ajv.compile(schema);
test({"foo": ""});
function test(data) {
var valid = validate(data);
if (valid) console.log('Valid!');
else console.log(validate.errors);
}
result is : valid

You can configure Ajv to throw errors and use compile:
var ajv = new Ajv({
allErrors: true
});
var schema = {
type: 'object',
properties: {
date: {
type: 'unexisting-type'
}
}
};
try {
var validate = ajv.compile(schema);
} catch (e) {
console.log(e.message);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.6.2/ajv.min.js"></script>

above mention schema are valid schema according to ajv.validateSchema().
It's valid but it validated nothing, if you want to test a simple object with a foo mandatory property, you can do something like that :
var ajv = new Ajv({
allErrors: true
});
var schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"foo": {
"type": "string",
"minLength": 3,
"maxLength": 255
}
},
"required": [
"foo"
]
};
try {
var validate = ajv.compile(schema);
test({"foo": "" });
} catch (e) {
console.log("Validate error :" + e.message);
}
function test(data) {
var valid = validate(data);
if (valid) {
console.log('Valid!');
} else {
console.log(validate.errors);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.6.2/ajv.min.js"></script>
Run with data = {"foo": "" } return the error message below :
[
{
"keyword": "minLength",
"dataPath": ".foo",
"schemaPath": "#/properties/foo/minLength",
"params": {
"limit": 3
},
"message": "should NOT be shorter than 3 characters"
}
]
Run with data = {"foo": "abcdef" } return the message below :
Valid!

Related

Node - build a tree recursively with API data

I need to build a tree like structure using data from an API.
The structure i start with is as follows:
{
"type": "group",
"id": 1,
"name": "rootGroup",
"members": [],
}
There will always be a root group as the base of the tree.
I have a function named getMembersInGroup(groupId) which is an API call and returns something like:
[
{
"type": "group",
"id": 77,
"name": "IT group",
},
{
"type": "user",
"id": 40,
"name": "John"
}
]
Members can either be of type user or another group. So a user would look like:
{
"type": "user",
"id": 40,
"name": "John"
}
If it's another group it needs to recursively fetch those until there are only users or empty array left in members.
Any group can have users at any level with the tree.
A mock of getMembersInGroup:
const getMembersInGroup = async (groupId) => {
try {
const members = await fetch.callApi('/groups/' + groupId + '/members');
if (members) {
return members;
}
else {
return [];
}
} catch (error) {
return { error };
}
}
The end result should look like this:
{
"type": "group",
"id": 1,
"name": "rootGroup",
"members": [
{
"type": "group",
"id": 88,
"name": "Some group",
"members": [
{
"type": "user",
"id": 231,
"name": "SALLY"
},
{
"type": "user",
"id": 232,
"name": "Henry"
}
]
},
{
"type": "user",
"id": 41,
"name": "Chris"
}
],
}
I need help with the algorithm to create the tree.
Your getMembersInGroup function could look like this:
const getMembersInGroup = async (groupId) => {
const members = (await fetch.callApi(`/groups/${groupId}/members`)) ?? [];
for (const member of members) {
if (member.type == "group") {
member.members = await getMembersInGroup(member.id);
}
}
return members;
}
Call it like this:
async function loadTree() {
return {
type: "group",
id: 1,
name: "rootGroup",
members: await getMembersInGroup(1)
};
}
loadTree().then(result =>
console.log(result);
// Work with the result ...
).catch(error =>
console.log("error: ", error)
);
Demo with a mock implementation of fetch.callApi:
// Mock for fetch.callApi
const fetch = {
mockData: [0,[2,3,4],[5,6,7],[8,9],0,0,0,[10],0,0,[11,12],0,0],
callApi(url) {
return new Promise((resolve, reject) => {
const groupId = +url.split("/")[2];
const children = this.mockData[groupId];
if (!children) return reject("not found: " + groupId);
const result = children.map(id => {
const type = this.mockData[id] ? "group" : "user";
return {type, id, name: type + "_" + id};
});
setTimeout(() => resolve(result), 50);
});
}
}
async function loadTree() {
return {
type: "group",
id: 1,
name: "rootGroup",
members: await getMembersInGroup(1)
};
}
const getMembersInGroup = async (groupId) => {
const members = (await fetch.callApi('/groups/' + groupId + '/members')) ?? [];
for (const member of members) {
if (member.type == "group") {
member.members = await getMembersInGroup(member.id);
}
}
return members;
}
loadTree().then(result =>
console.log(JSON.stringify(result, null, 2))
).catch(error =>
console.log("error: ", error)
);
You can do something like:
const getMembersInGroup = async (groupId) => {
try {
const members = await fetch.callApi('/groups/' + groupId + '/members');
if (members) {
foreach(member in members) {
if (member.type == 'groups') {
member.members = getMembersInGroup(member.groupid)
}
}
return members;
}
else {
return [];
}
} catch (error) {
return { error };
}
}
So you have the recursion only if it's a group type, otherwise the member is returned as is.

Null value in model.findById when I make a get request [mongodb]

Problem
Hi dev,
I have the problem that when I try to make a get request to the series by id it shows me null.
I have noticed from the Atlas Mongos platform that I created the collection but it does not show me the data, only the structure of the scheme shows me
Function.js
const fs = require('fs');
const fetch = require('node-fetch');
const BASE_URL = " http://localhost:8081/api/v1/"
async function getSeries() {
return new Promise((resolve , reject) =>{
setTimeout(() => {
const res = require('./simple_database/series/1.json' , 'utf8');
resolve(res)
}, 1000);
})
}
module.exports = {
getSeries
}
Router
The route allseries allows me to access all the content. What I want to do is pass that content to the SeriesModel, maybe it is there where I have the problem that the data is not being inserted correctly.
In the route series/:id is where the null value is returning to me
const express = require('express');
const router = express.Router();
const f = require('./function');
const SeriesModel = require('./models/series');
router.get('/allseries', (req, res) => {
f.getSeries().then((series) =>{
res.status(200).json({
series
})
}).then((doc) =>{
SeriesModel.insertMany(doc , function(err , docs){
if(err){
console.error(err)
}else{
console.log(docs);
console.info('%d serie were successfully stored.', docs.length);
}
})
})
});
router.get('/series/:id' , (req , res , next) =>{
const id = req.params.id;
SeriesModel.findById(id)
.exec()
.then((doc) =>{
console.log("From database " , doc);
res.status(200).json(doc)
}).catch((err) =>{
console.error(err);
res.status(500).json({error: err})
})
})
module.exports = router;
Model/series.js
const mongoose = require('mongoose');
const serieSchema = mongoose.Schema({
"_id": {
"$oid": {
"type": "ObjectId"
}
},
"series_id": {
"type": "String"
},
"aggregateRating": {
"reviewCount": {
"type": "Number"
},
"ratingCount": {
"type": "Number"
},
"#type": {
"type": "String"
},
"ratingValue": {
"type": "Number"
}
},
"episodes": {
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
},
"metadata": {
"description": {
"type": "String"
},
"url": {
"type": "String"
},
"image": {
"type": "String"
},
"type": {
"type": "String"
},
"id": {
"type": "String"
},
"name": {
"type": "String"
}
},
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
});
module.exports = mongoose.model("cr_series" , serieSchema);
It is because findById takes it's parameter in form of object like this
SeriesModel.findById({_id:id})
You need to tell your query to which json object you want to match your incoming object.

Can I validate a date using ajv json schema, without converting the date to string?

I have an object which contains one or more properties of type date. I would like to validate the object using the ajv json schema validator package.
I could convert the properties of type date to a string by using the toISOString(). But the object can be quiet big and thus I dont want to convert all the date-properties of the whole object.
Is there a solution other than converting the date to a string?
Could I somehow create a custom ajv schema validator?
// My example schema
const schema = {
"properties": {
"createdAt": {
"type": "string",
"format": "date-time"
},
"lastName": { "type": "string" },
"firstName": { "type": "string" }
}
};
// My example testobject
const testObj = {
createdAt: new Date(),
lastName: "Doe",
firstName: "John"
}
// The validation
const validate = ajv.compile(schema);
const valid = validate(testObj);
if(!valid) console.log('Invalid: ' + ajv.errorsText(validate.errors));
This will do a console log, because the testObj.createdAt is a date and not a string.
Simply change your ajv schema from "type": "string" to "type": "object" and the built-in ajv date-time format will work. Here's a working fiddle.
You can also define a custom ajv format to validate a Date object (or string) like this:
ajv = new Ajv();
ajv.addFormat('custom-date-time', function(dateTimeString) {
if (typeof dateTimeString === 'object') {
dateTimeString = dateTimeString.toISOString();
}
return !isNaN(Date.parse(dateTimeString)); // any test that returns true/false
});
... and invoke your custom format in the ajv schema like this:
const schema = {
"properties": {
"createdAt": {
"type": "object",
"format": "custom-date-time"
Putting it all together here's the code and a working fiddle for creating a custom date format:
// My example schema
const schema = {
"properties": {
"createdAt": {
"type": "object",
"format": "custom-date-time"
},
"lastName": {
"type": "string"
},
"firstName": {
"type": "string"
},
"required": [ 'createdAt', 'lastName', 'firstName' ],
"additionalProperties": false,
}
};
// My example testobject
const testObj = {
createdAt: new Date(),
lastName: "Doe",
firstName: "John"
}
// The validation
ajv = new Ajv();
ajv.addFormat('custom-date-time', function(dateTimeString) {
if (typeof dateTimeString === 'object') {
dateTimeString = dateTimeString.toISOString();
}
return !isNaN(Date.parse(dateTimeString)); // return true/false
});
const validate = ajv.compile(schema);
const valid = validate(testObj);
if (valid)
alert('valid');
else
alert('Invalid: ' + ajv.errorsText(validate.errors));
One of the valid way is to convert first to JSON Schema-compatible object.
function makeJsonSchemaCompatible (obj) {
if (Array.isArray(obj)) {
return obj.map((subObj) => makeJsonSchemaCompatible(subObj))
} else if (obj && typeof obj === 'object') {
const replacement = {}
const className = obj.constructor.name
if (className !== 'Object') {
replacement.__className = className
}
Object.entries(obj).map(([k, v]) => { replacement[k] = makeJsonSchemaCompatible(v) })
return replacement
}
return obj
}
Unfortunately the format property does not proc' for object types, so custom formats aren't an option. However, I was able to add a custom keyword (inspired by looking at the instance keyword, which actually gets you half way there) that gave me the desired results (that being the value must be a Date object and the Date must be valid).
const { equal } = require('assert');
const Ajv = require('ajv');
const { _ } = require('ajv');
const ajv = new Ajv();
const schema = {
type: 'object',
properties: {
jsdate: {
type: 'object',
isDate: true,
},
},
};
ajv.addKeyword({
keyword: 'isDate',
type: 'object',
code(ctx) {
const { data } = ctx;
ctx.pass(_`${data} instanceof Date && !isNaN(+${data})`);
},
});
const validate = ajv.compile(schema);
equal(validate({ jsdate: new Date() }), true, 'should succeed');
equal(validate({ jsdate: {} }), false, 'should fail for random object');
equal(
validate({ jsdate: '2001-01-01' }),
false,
'should fail for valid date string'
);
equal(
validate({ jsdate: new Date('rubbish') }),
false,
'should fail if Date is invalid'
);
It seems that you could achieve the expected result by using the instanceof keyword (part of ajv-keywords) :
.
const Ajv = require("ajv");
const addKeywords = require("ajv-keywords");
const ajv = new Ajv(); // options can be passed, e.g. {allErrors: true}
addKeywords(ajv);
// My example schema
const schema = {
type: "object",
properties: {
createdAt: {
instanceof: "Date",
},
lastName: { type: "string" },
firstName: { type: "string" },
},
};
// My example testobject
const testObj = {
createdAt: new Date(),
lastName: "Doe",
firstName: "John",
};
// The validation
const validate = ajv.compile(schema);
const valid = validate(testObj);
if (!valid) console.log("Invalid: " + ajv.errorsText(validate.errors));

How to call Web3js function from Nodejs file

I have the following working web3js code, Calling App.createContract() on button click works well on a webpage, however I want to call App.createContract() or similar from another Nodejs controller. Infact what I am thinking of is making an API in Node which could call the web3js function and returns a JSON result back to the caller. Can you please help me how to import my web3js file and call the function from Node Controller? Thanks
import "../stylesheets/app.css";
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';
import { default as CryptoJS} from 'crypto-js';
var accounts;
var account;
var shLogABI;
var shLogContract;
var shLogCode;
var shLogSource;
window.App = {
start: function() {
var self = this;
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}
if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}
accounts = accs;
console.log(accounts);
account = accounts[0];
web3.eth.defaultAccount= account;
shLogSource= "pragma solidity ^0.4.6; contract SHLog { struct LogData{ string FileName; uint UploadTimeStamp; string AttestationDate; } mapping(uint => LogData) Trail; uint8 TrailCount=0; function AddNewLog(string FileName, uint UploadTimeStamp, string AttestationDate) { LogData memory newLog; newLog.FileName = FileName; newLog.UploadTimeStamp= UploadTimeStamp; newLog.AttestationDate= AttestationDate; Trail[TrailCount] = newLog; TrailCount++; } function GetTrailCount() returns(uint8){ return TrailCount; } function GetLog(uint8 TrailNo) returns (string,uint,string) { return (Trail[TrailNo].FileName, Trail[TrailNo].UploadTimeStamp, Trail[TrailNo].AttestationDate); } }";
web3.eth.compile.solidity(shLogSource, function(error, shLogCompiled){
shLogABI = JSON.parse(' [ { "constant": false, "inputs": [ { "name": "TrailNo", "type": "uint8" } ], "name": "GetLog", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "FileName", "type": "string" }, { "name": "UploadTimeStamp", "type": "uint256" }, { "name": "AttestationDate", "type": "string" } ], "name": "AddNewLog", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "GetTrailCount", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "type": "function" } ]');
shLogContract = web3.eth.contract(shLogABI);
});
});
},
createContract: function()
{
shLogContract.new("", {from:account, gas: 3000000}, function (error, deployedContract){
if(deployedContract.address)
{
document.getElementById("contractAddress").value=deployedContract.address;
document.getElementById("fileName").value = '';
document.getElementById("uploadTimeStamp").value = '';
document.getElementById("attestationDate").value = '';
}
})
},
addNewLog: function()
{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
var fileName = document.getElementById("fileName").value;
var uploadTimeStamp = document.getElementById("uploadTimeStamp").value;
var attestationDate = document.getElementById("attestationDate").value;
deployedshLog.AddNewLog(fileName, uploadTimeStamp, attestationDate, function(error){
console.log(error);
})
},
getLog: function()
{
try{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
deployedshLog.GetTrailCount.call(function (error, trailCount){
deployedshLog.GetLog.call(trailCount-1, function(error, returnValues){
document.getElementById("fileName").value= returnValues[0];
document.getElementById("uploadTimeStamp").value = returnValues[1];
document.getElementById("attestationDate").value=returnValues[2];
})
})
}
catch (error) {
document.getElementById("fileName").value= error.message;
document.getElementById("uploadTimeStamp").value = error.message;
document.getElementById("attestationDate").value= error.message;
}
}
};
window.addEventListener('load', function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.start();
});
Simple example for web3js 1.0.0.beta*
1) Add web3js to your package.json on a server side:
"web3": "^1.0.0-beta.27"
2) Require and init web3 with some provider:
const Web3 = require('web3');
const web3SocketProvider = new Web3.providers.WebsocketProvider('ws://0.0.0.0:8546');
const web3Obj = new Web3(web3Provider);
Now you can use your web3 object as usual:
async function getAccounts() {
let accounts = await web3Obj.eth.getAccounts();
}

How to remove and update a XML tag in xml2js

How can I edit XML file by xml2js
const fs = require('fs');
const xml2js = require('xml2js');
var fn = 'file.xml';
fs.readFile(fn, function(err, data) {
parser.parseString(data, function(err, result) {
//result.data[3].removeChild(); ?????
//result.date[2].name.innerText = 'Raya'; ?????
});
});
This is not working!
To remove a property from a JavaScript object, simply set the property to undefined:
result.name = undefined;
Result from xml2js:
{
"name": "I will get deleted",
"items": [
{
"itemName": "Item 1",
"itemLocation": "item1.htm"
},
{
"itemName": "Item 2",
"itemLocation": "item2.htm",
}
]
}
After setting to undefined:
{
"items": [
{
"itemName": "Item 1",
"itemLocation": "item1.htm"
},
{
"itemName": "Item 2",
"itemLocation": "item2.htm",
}
]
}
For you, this would be
result.data[3] == undefined;
Tip: you can use JSON.stringify to help you.

Resources