JayData - OData query gets "$data.Object not convertible to $data.ObjectID" - node.js

I've created an OData Endpoint with Node by using odata-server module by JayData in this way:
require("odata-server");
$data.Entity.extend("Service", {
Id: {type: "id", key: true, computed: true, nullable: false},
Name: {type: "string", nullable: false, maxLength: 50}
});
$data.EntityContext.extend("marketplace", {
Services: {type: $data.EntitySet, elementType: Service}
});
$data.createODataServer(marketplace, "/marketplace", 8081, "localhost");
console.log("Marketplace OData Endpoint created... Listening at 8081.");
Then, still with Node, I've created an Express web application which receives some commands through GET request, connects to the OData Endpoint (still by using JayData) and receives some data from there, then sends back the result to the client (in the following code it just sends 200), in this way (by defining a route):
require("jaydata");
...
app.get("/addCompare/:id", function(req, res) {
console.log("Comparison request for: " + req.params.id);
$data.Entity.extend("Service", {
Id: {type: "id", key: true, computed: true, nullable: false},
Name: {type: "string", nullable: false, maxLength: 50}
});
$data.EntityContext.extend("marketplace", {
Services: {type: $data.EntitySet, elementType: Service}
});
db = new marketplace("http://localhost:8081/marketplace");
db.onReady(function() {
var arr = db.Services.filter(function(s) {return s.Name.startsWith("Serv");}).toArray();
console.dir(arr);
});
res.send(200);
});
The problem is that when I try this code (by using this GET request for example: http://www.localhost:8080/addCompare/NTM0M2ZkNjU2YjljNWMwODRiOGYyYTU5), I always get this error in the server and after that it crashes. Here's the error:
TypeError: Value '$data.Object' not convertable to '$data.ObjectID'
{ name: 'TypeError',
message: 'Value \'$data.Object\' not convertable to \'$data.ObjectID\'',
data:
{ __metadata:
{ type: 'Service',
id: 'http://localhost:8081/marketplace/Services(\'NTM0M2ZkNjU2YjljNWMwODRiOGYyYTU5\')',
uri: 'http://localhost:8081/marketplace/Services(\'NTM0M2ZkNjU2YjljNWMwODRiOGYyYTU5\')' },
Id: 'NTM0M2ZkNjU2YjljNWMwODRiOGYyYTU5',
Name: 'Service51' } }
Where am I wrong? Thanks...

As the behavior was explained in OData - Strange index with MongoDB [Mongoose: Cast Error], the id - NTM0M2ZkNjU2YjljNWMwODRiOGYyYTU5 – should be base-64 decoded (for example 5343fd656b9c5c084b8f2a70 is a valid format).
Although the declaration of JayData model is correct, it will be re-defined every single time when a request arrives to your server. You can improve the current implementation by moving your $data.Entity.extend and $data.EntityContext.extend blocks outside the app.get – after require("jaydata");.

Related

Mongoose 5.0.15 / MongoDB 3.6.4 Cast to Array Failed for value [NaN, NaN]

Context
This is not a duplicate of this
As explained in detail below I scoured the web and read about 20 similar bugs or articles and nothing out there has solved this issue, as yet.
I am developing a location-based application in two different OS, Windows 8.1 and Linux/Ubuntu (16.4)
The following engines are running in the Ubuntu machine; however, the error I get is exactly the same regardless of dependency version or OS.
Node.js ~4.2.6
NPM ~ 3.5.2
MongoDB ~ 3.6.4
Mongoose ~5.0.15
What is the current behavior?
I am getting this error, despite having spent days researching similar bugs and testing/trying different things.
From Postman:
{
"errors": {
"coords": {
"message": "Cast to Array failed for value \"[ NaN, NaN ]\" at path \"coords\"",
"name": "CastError",
"stringValue": "\"[ NaN, NaN ]\"",
"kind": "Array",
"value": [
null,
null
],
"path": "coords",
"reason": {
"message": "Cast to [number] failed for value \"[null]\" at path \"coords\"",
....
"_message": "Location validation failed",
"message": "Location validation failed: coords: Cast to Array failed for value \"[ NaN, NaN ]\" at
path \"coords\", name: Path `name` is required., openingTimes.0.days: Path `days` is required.,
openingTimes.0.closed: Path `closed` is required.",
"name": "ValidationError"
}
My Assumption here is that this is not just a problem due to Cast Error but that something is wrong with the Schema for this specific POST method. Note that the same Schema is used for GET methods and it works like charm.
Steps to reproduce.
At this point of the development I am designing the API and am using Mongoose's Schema to model the data.
This is the model for the API:
const` mongoose = require("mongoose");
const reviewSchema = new mongoose.Schema({
author: String,
rating: {$type: Number, required: true, min: 0, max: 5},
timestamp: {$type: Date, "default": Date.now},
reviewText: String
}, {typeKey: "$type"});
const openingTimeSchema = new mongoose.Schema({
days: {$type: String, required: true},
opening: String,
closing: String,
closed: {$type: Boolean, required: true}
}, {typeKey: "$type"});
const locationSchema = new mongoose.Schema({
name: {$type: String, required: true},
rating: {$type: Number, "default": 0, min: 0, max: 5},
address: String,
facilities: [String],
coords: {$type: [[Number]], index: '2dsphere', required:true},
openingTimes: [openingTimeSchema],
reviews: [reviewSchema]
}, {typeKey: "$type"});
mongoose.model("Location", locationSchema);
Note I am using typeKey as suggested in different posts and as suggested as well in Mongoose documentation for geoJSON objects. Even without using typeKey the error persist.
Controller
const mongoose = require("mongoose");
const Loc = mongoose.model("Location");
const theEarth = (function(){
const earthRadious = 6371;
const getRadsFromDistance = (distance) => {
return parseFloat(distance / earthRadious);
};
return {
getRadsFromDistance: getRadsFromDistance
};
})();
var sendJsonResponse = (res, status, content) =>{
res.status(status);
res.json(content);
};
module.exports.locationsCreate = (req, res) => {
Loc.create({
name: req.body.name,
address: req.body.address,
facilities: req.body.facilities,
coords: [parseFloat(req.body.lng), parseFloat(req.body.lat)],
openingTimes: [{
days: req.body.days1,
opening: req.body.opening1,
closing: req.body.closing1,
closed: req.body.closed1
}]
}, function (err, location){
if (err){
sendJsonResponse(res, 400, err);
}
else{
sendJsonResponse(res, 201, location);
}
});
};
The expected behavior is to POST the dummy location created in Postman:
POST /api/locations HTTP/1.1
Host: localhost:3000
name: Coffee BlaBlaBla
address: 435 High Street Paradise, Midnowhere
facilities: Hot Coffee,Breakfast,Wifi
lng: 2.3567
lat: 41.5676
days1: Monday - Friday
opening1: 8:00am
closing1: 5:00pm
closed1: false
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
If someone with a fresh eye knows what is going, that would be phenomenal.
Thank you
This issue is now solved. As suspected, this had nothing to do with previous questions asked here and in GitHub. The problem was the request passed through Postman wasn't fit for the mongoose schema. This was because of the validation requirements that various documents' fields had and so it only took one tiny error in formulating each request to throw the error.
Once I started testing the req.body elements one by one, I got continuous errors but the server, however, was starting to log some fields correctly, although in isolation and one by one. That was an eye opener to test all the fields until I found the right combination. Counter-intuitively the req.body returned an empty object at first because the request was broken in the first instance.
All the previous attempts i.e. changing type for keyType, using the body-parser instead of the express built-in middleware to process JSON and URLs requests, etc, were good attempts but all futile.
Thank you all for contributing and looking into this with me.

Mongoose Schema validation issues when using Array of Mixed [{ }]

I am building a schema in mongoose (v4.13.8) with an Array of Mixed values. I have come up with the following Schema:
var deviceConfigSchema = new mongoose.Schema({
capabilities: {
type: [capabilitySchema],
required: true,
validator: [isValidCapabilities, "Not a valid capability array"]
},
services: {
type: [{}],
required: true,
validator: [isValidServices, "Not a valid service array"]
}
});
The problem is that I get a validation error saying that services: Path 'services' is required. when I try to submit data. What is strange is that the data I send for the 'capabilities' works fine and the only difference is that I specify a schema explicitly.
Removing the required: true from services causes there to be an empty array object in the returned values.
I am submitting the data using an API POST request with the data in the body of the request. I am using Postman to submit the request, with x-www-form-urlencoded checked. This is copied from the body key-value input
capabilities[0][field_map][field]:pressure
capabilities[0][field_map][type]:float
capabilities[0][field_map][format]:hPa
services[0][name]:rest
services[0][receive][0][capability_id]:0
services[0][receive][0][path]:/api/relay/0
Update:
I'd like to apologise as this was a mistake on my part. I dynamically create a configuration based on the request and at one point the copied services were being made null, doh!
However, having got the required: true validation to pass, the custom validator is still not being executed. I also can't find any documentation about the order in which validators and are executed which would be very useful. Below is the validator snippet for reference:
function isValidServices(services) {
for (const service of services) {
if (typeof service.name !== 'string') return false;
}
return true;
}
Having experimented with various approaches and looking in more detail on the mongoose API docs, I found that there is a validate option for schemas too. http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate
I changed my Schema from this:
var deviceConfigSchema = new mongoose.Schema({
capabilities: {
type: [capabilitySchema],
required: true,
validator: [isValidCapabilities, "Not a valid capability array"]
},
services: {
type: [{}],
required: true,
validator: [isValidServices, "Not a valid service array"]
}
});
To this [notice the validate instead of validator]...
var deviceConfigSchema = new mongoose.Schema({
capabilities: {
type: [capabilitySchema],
required: true,
validate: [isValidCapabilities, "Not a valid capability array"]
},
services: {
type: [{}],
required: true,
validate: [isValidServices, "Not a valid service array"]
}
});
After this my validator functions were being executed without any issues. Hopefully this helps someone.

node.js odata-server mongodb unable to post related entity

I have been working on a node.js odata server based on this example: How to set up a nodejs OData endpoint with odata-server
I have everything working... I can read, update, insert, delete. But I am trying to associate a Journal with a Tasks and I am having problems.
I have tried several different ways outlined here: Operations (OData Version 2.0)
Here is my code:
/* global $data */
require('odata-server');
$data.Class.define("Task", $data.Entity, null, {
Id: { type: "id", key: true, computed: true, nullable: false },
Title: { type: "string", required: true, maxLength: 200 },
Journals: { type: "array", elementType: "Journal"
, inverseProperty: "Task" }
});
$data.Class.define("Journal", $data.Entity, null, {
Id: { type: "id", key: true, computed: true, nullable: false },
Entry: { type: "string" },
DateInserted: { type: "string" },
Task: { type: "object", elementType: "Task" , inverseProperty: "Journals" }
});
$data.EntityContext.extend("obb", {
Tasks: { type: $data.EntitySet, elementType: Task },
Journals: { type: $data.EntitySet, elementType: Journal }
});
$data.createODataServer(obb, '/api-v0.1', 2046, 'localhost');
Question:
Is this feature even available from odata-server what would the post look like to link a Journal to a Task?
I am using fiddler2 and composing a POST I have tried these urls:
//localhost:2046/api-v0.1/Tasks('the-id-of-a-task')/Journals
//localhost:2046/api-v0.1/Tasks('the-id-of-a-task')/Journals/$link
post body's I have tried:
{"Entry":"This is a test"}
{"url":"http://localhost:2046/api-v0.1/Journals('id-of-a-journal-in-the-db')"}
I have even tried to build out and post a Task with journals together and that didn't work.
Any help would be greatly appreciated. Thanks.

Node.js waterline-orientdb update fail

I am trying to create a simple server application in Node.js using the waterline-orientdb package where there are several users who can invoke several methods. Before a user can do anything, the user needs to authenticate with his username and password. Within this authentication the user object is given a token that will be piggybacked with the future requests.
When a user is given a token, an update query is invoked. When invoking the update request I get the following error:
ERROR err: { [OrientDB.RequestError: expression item ']' cannot be resolved because current record is NULL]
name: 'OrientDB.RequestError',
message: 'expression item \']\' cannot be resolved because current record is NULL',
data: {},
previous: [],
id: 1,
type: 'com.orientechnologies.orient.core.exception.OCommandExecutionException',hasMore: 0 }
The strange thing is that the update is executed, so this error doesn't have influence on the update request. But because I want to catch all errors, I can't just ignore this.
My model looks like this:
module.exports = {
tableName: 'User',
identity: 'dbuser',
schema: true,
attributes: {
id: {
type: 'string',
primaryKey: true,
columnName: '#rid'
},
username: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string',
required: false
},
token: {
type: 'string'
},
follows: {
collection: 'dbuser',
via: 'followed',
dominant: true
},
followed: {
collection : 'dbuser',
via: 'follows'
}
};
As you can see, I'm associating two users with eachother so that one user can follow the activities of the other user. When I delete the association (so follows and followed) the error also dissapears.
The piece of code where the updates happens looks like this:
user[0].token = generateToken(user[0])
dbuser.update({
id: user[0].id
}, user[0]).exec(function (error, data) {
if (error) res.json(401, {
code: 401,
error: "Token could not be updated"
})
res.json(user);
});
Does anyone has an idea on how to avoid this behavior or what the error even means?
It seems to be a bug in the adapter.
You could try using:
npm install appscot/waterline-orientdb#refactor_collection
Apparently will be resolved in v.0.10.40
More info about it: https://github.com/appscot/waterline-orientdb/issues/43#issuecomment-75890992

Mongoose error findByIdAndUpdate fails in cast

Trying to update a document using findByIdAndUpdate, i get an error that i don't understand.
console.log(req.body);
var data = req.body;
data._id = undefined;
Package.findByIdAndUpdate(req.params.id, data, function (err, pkg) {
if (err) {
console.log(err.stack);
return next(restify.InternalServerError(err));
}
res.json(pkg);
next();
});
I get the following error:
TypeError: Cannot read property '_id' of undefined
at ObjectId.cast (/home/ubuntu/workspace/server/node_modules/mongoose/lib/schema/objectid.js:109:12)
at ObjectId.castForQuery (/home/ubuntu/workspace/server/node_modules/mongoose/lib/schema/objectid.js:165:17)
at Query._castUpdateVal (/home/ubuntu/workspace/server/node_modules/mongoose/lib/query.js:2009:17)
at Query._walkUpdatePath (/home/ubuntu/workspace/server/node_modules/mongoose/lib/query.js:1969:25)
at Query._castUpdate (/home/ubuntu/workspace/server/node_modules/mongoose/lib/query.js:1865:23)
at castDoc (/home/ubuntu/workspace/server/node_modules/mongoose/lib/query.js:2032:18)
at Query._findAndModify (/home/ubuntu/workspace/server/node_modules/mongoose/lib/query.js:1509:17)
at Query.findOneAndUpdate (/home/ubuntu/workspace/server/node_modules/mongoose/node_modules/mquery/lib/mquery.js:2056:15)
at Function.Model.findOneAndUpdate (/home/ubuntu/workspace/server/node_modules/mongoose/lib/model.js:1250:13)
at Function.Model.findByIdAndUpdate (/home/ubuntu/workspace/server/node_modules/mongoose/lib/model.js:1344:32)
I have verified that the id is valid, data is a valid object as well.
My model:
mongoose.model('Package', {
name: {
required: true,
type: String
},
servers: [mongoose.Schema.Types.ObjectId],
packageType: {
type: String,
enum: ['package', 'subscription']
},
subscriptionPeriodInDays: Number,
pointsIncluded: Number,
price: Number,
rank: String,
data: mongoose.Schema.Types.Mixed //For custom solutions
});
The log also prints a valid data object
{
name: 'Your Package',
packageType: 'subscription',
subscriptionPeriodInDays: 30,
pointsIncluded: 10000,
price: 10,
rank: 'Donator',
_id: undefined,
__v: 0,
servers: [],
description: '<p>test</p>\n'
}
I have tried to step trough with the debugger but i couldn't find a reason for this.
As Raleigh said, you need to remove _id field. You can do it by delete data._id; instead of data._id = undefined;.
I believe Mongoose is trying to set the value of _id to undefined since the _id value is still getting passed in via the data object.
Try removing the line data._id = undefined; before you update the model or completely remove the _id field from the data object.

Resources