Using MikroORM with Jest toMatchObject matcher ends with error: TypeError: Cannot redefine property: __helper - jestjs

Sometimes when using MikroORM entities with Jest (TS) in this way:
const ads = await repo.findAll({
orderBy: {
providerId: 'asc'
}
})
expect(ads).toMatchObject([
{
providerId: 'a',
title: 'a-changed',
createdBy: {
id: firstJobId
},
updatedBy: {
id: secondJobId
},
removed: false
}])
it causes this error to be thrown:
Cannot redefine property: __helper TypeError: Cannot redefine
property: __helper
When I try to use wrap(entity).toPOJO() a reference to another entity is no longer an object but is converted to an id. In my case the createdBy and updatedBy becomes a number.
Is there a way around this problem?

Related

AJV JsonSchema validator reports seemingly incorrect error

I'm using AJV to validate a HTTP request payload against a schema. However, I see an error reported that I was not expecting. This is a code example to demonstrate the issue:
const schema = {
type: 'array',
minItems: 1,
items: {
anyOf: [
{
type: 'object',
properties: {
op: {
enum: ['replace']
},
path: {
type: 'string',
pattern: '/data/to/foo/bar',
},
value: {
type: 'string',
},
},
},{
type: 'object',
properties: {
op: {
enum: ['replace']
},
path: {
type: 'string',
pattern: '/data/to/baz',
},
value: {
type: 'object',
required: ['foo', 'bar'],
properties: {
foo: {
type: 'string',
},
bar: {
type: 'string',
},
}
}
}
}
],
},
}
const validator = new ajv()
const compiledValidator = validator.compile(schema)
const data = [
{ // this object should pass
op: 'replace',
path: '/data/to/foo/bar',
value: 'foo',
},
{ // this object should fail in the `value` mismatch (missing required attribute)
op: 'replace',
path: '/data/to/baz',
value: {
foo: 'bar',
},
},
]
compiledValidator(data)
console.log(compiledValidator.errors)
The schema defines a number of objects to which an incoming list of data objects should match. The first data item matches the schema (first item schema), however the second data item misses a required attribute (bar) in the value object.
When I run the above code I get the following output:
[
{
instancePath: '/1/path',
schemaPath: '#/items/anyOf/0/properties/path/pattern',
keyword: 'pattern',
params: { pattern: '/data/to/foo/bar' },
message: 'must match pattern "/data/to/foo/bar"'
},
{
instancePath: '/1/value',
schemaPath: '#/items/anyOf/1/properties/value/required',
keyword: 'required',
params: { missingProperty: 'bar' },
message: "must have required property 'bar'"
},
{
instancePath: '/1',
schemaPath: '#/items/anyOf',
keyword: 'anyOf',
params: {},
message: 'must match a schema in anyOf'
}
]
I understand the 2nd and the 3rd (last) errors. However, The first error seems to indicate that the path doesn't match path requirements of the first item schema. It is true that the 2nd data item doesn't match the 1st schema item but I don't seem to understand how it is relevant. I would assume that the error would be focused around the value, not the path since it matches on the path schemas.
Is there a way to get the error reporting more focused around the errors that matter?
There is no way for the evaluator to know whether you intended the first "anyOf" subschema to match or the second, so the most useful thing to do is to show you all the errors.
It can be confusing because you don't need to resolve all the errors, just some of them, which is why some implementations also offer a heirarchical error format to make it more easy to see relationships like this. Maybe if you request that ajv implement more of these error formats, it will happen :)
You can see that all of the errors pertain to the second item in the data by looking at the instancePath for each error, which all start with /1. This is the location within the data that generated the error.
So let's look at the second item and compare it against the schema.
{ // this object should fail in the `value` mismatch (missing required attribute)
op: 'replace',
path: '/data/to/baz',
value: {
foo: 'bar',
},
}
The schema says that an item should either (from the anyOf) have
path: '/data/to/foo/bar' and value: { type: 'string' }, or
path: '/data/to/baz' and value: { required: [ 'foo', 'bar' ] }
The reported errors are:
The first case fails because path is wrong.
The second case fails because /value/bar is not present.
The last error is just the anyOf reporting that none of the options passed.

Is there a way to validate a UUID inserted in a mongoDB using mongo's validator?

I am using migrate-mongo for managing my database migration and I am trying to create a new migration that create a collection with a validator and insert values in it. I want to use a UUID for the _id property and I am using the uuid-mongodb library to generate it. My problem is that I am not able to set the bsonType of my _id in the validator without causing the data insertion failure. Is there any way to make sure that the id of the documents inserted in the collection is a UUID? I know that mongoose could help me to solve this issue, but I would like the validation to be done at the database level. Also, when I do not specify the _id's bsonType in the validator, the insertion works, it fails validation only when I specify it.
Here is my migration code
const MUUID = require("uuid-mongodb");
module.exports = {
async up(db) {
//Will use https://docs.mongodb.com/manual/tutorial/model-tree-structures-with-materialized-paths/
await db.createCollection("itemCategories", {
validator: {
$jsonSchema: {
required: ["name"],
bsonType: "object",
properties: {
_id: {"object"}, //I also tried with binData
name: {
bsonType: "string",
maxLength: 50,
},
path: {
bsonType: ["string", "null"],
pattern: "^,([^,]+,)+$"
}
},
additionalProperties: false,
}
},
});
await db.collection("itemCategories").createIndex({"name": 1}, {unique: true});
await db.collection("itemCategories").insertMany([
{_id: MUUID.v4(), name: "Sport", path: null},
{_id: MUUID.v4(), name: "Tool", path: null},
{_id: MUUID.v4(), name: "Entertainment", path: null}
]);
},
async down(db) {
await db.dropCollection("itemCategories");
}
};
And here is the error I get when running it
ERROR: Could not migrate up 20210627041314-create-categories.js: Document failed validation BulkWriteError: Document failed validation
at OrderedBulkOperation.handleWriteError (C:\Users\username\projectDirectory\node_modules\mongodb\lib\bulk\common.js:1352:9)
at resultHandler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\bulk\common.js:579:23)
at handler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\core\sdam\topology.js:943:24)
at C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\connection_pool.js:350:13
at handleOperationResult (C:\Users\username\projectDirectory\node_modules\mongodb\lib\core\sdam\server.js:558:5)
at MessageStream.messageHandler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\connection.js:281:5)
at MessageStream.emit (events.js:321:20)
at processIncomingData (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\message_stream.js:144:12)
at MessageStream._write (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\message_stream.js:42:5)
at doWrite (_stream_writable.js:441:12)
Assuming collection name user_demo and having 2 fields only ( _id, name )
Create collection Schema Validator
db.createCollection("user_demo", {
validator: {
$jsonSchema: {
bsonType: "object",
title: "User Object Validation",
required: [ "_id","name"],
properties: {
_id: {
bsonType: "binData",
description: "Unique identifier,I am using it instead of objectId for portibility",
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
},
name: {
bsonType: "string",
description: "'name' must be a string and is required",
maxLength: 50,
minLength: 1
}
}
}
}
} )
Insert data in collection
a) If you already have a uuid4
db.user_demo.insertOne({_id: UUID("a5750db3-1616-45a4-bf92-6a44c3e67342"), name:"shiva"})
b) If you want random uuid4
db.user_demo.insertOne({_id: UUID(), name:"explore"})
Tested with mongo version 6.0.3

GraphQL showing Value as null [duplicate]

This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 3 years ago.
I am learning GraphQL and I have two Object types.
Say, they look like this
Say, The book type looks like this
const BookType = new GraphQLObjectType({
name: 'Book',
fields: () => ({
id: { type: GraphQLID},
name: { type: GraphQLString},
genre: { type: GraphQLString },
author: {
type: authorType,
resolve(parents, args) {
Author.findOne(
{
name: parents.authorName
}, function(err, result) {
console.log(result)
return result
})
}
}
})
})
and Author Type looks like this
const authorType = new GraphQLObjectType({
name: 'author',
fields: () => ({
id: { type: GraphQLID},
name: { type: GraphQLString},
age: { type: GraphQLInt },
books: {
type: new GraphQLList(BookType),
resolve(parent, args) {
}
}
})
})
Now, I am adding data through Mutation (Not sharing it because I think it is irrelevant) and then run query in graphql to add data in Book Type. It correctly displays data for name, genre, id but for authorType it is showing the data as null while the console].log results log something like this in console
//This is console log in terminal
{ age: 'none',
_id: 5bcaf8904b31d50a2148b60d,
name: 'George R Martin',
__v: 0 }
THis is the query I am running in graphiql
mutation{
addBooks(
name: "Game of Thrones",
genre: "Science Friction",
authorName: "George R Martin"
) {
name,
genre,
author {
name
}
}
}
My entire schema is available here
Can someone please-please help me figure out what could I be doing wrong?
A resolver must return either some value or a Promise that will resolve in a value -- if it doesn't the field being resolved will return null. So there's two things off about your code. One, you don't return either a value or a Promise. Two, you return something inside a callback, but that's not actually doing anything, since most libraries disregard the return value of a callback function anyway.
You can wrap a callback in a Promise, but that is going to be overkill here because mongoose already provides a way to return a Promise -- just omit the callback entirely.
resolve(parent, args) {
return Author.findOne({name: parent.authorName)
}
Your mutation resolver works because you return the value returned by calling save(), which actually returns a Promise that will resolve to the value of the model instance being saved.

GraphQLNonNull opposite

i`m trying implements graphql and i have problem.
I did type for graphql:
export const menuItemDataType = new GraphQL.GraphQLObjectType({
name: 'MenuItemData',
fields: () => ({
staticUrl: {
type: GraphQL.GraphQLString
},
page: {
type: new GraphQL.GraphQLNonNull(menuItemType),
resolve(MenuItemData) {
return PageRepository.getPageById(MenuItemData.page).exec();
}
},
menu: {
type: new GraphQL.GraphQLNonNull(menuType),
resolve(MenuItemData) {
return MenuRepository.getMenuById(MenuItemData.menu).exec();
}
}
})
})
and in this GraphQLObjectType i have page and menu.
I use mongoDB with mongoose. page and menu are nullable in model. When i query in graphql on this property so its chance that can be return null, but this is not compatible with GraphQL.GraphQLNonNull. Return error with "message": "Cannot return null for non-nullable field MenuItemData.page."
My question is: "Is any opposite for GraphQLNonNull. Like GraphQL?". I didnt found it.
Thank you
Don't use GraphQLNonNull if the type is nullable. GraphQL fields can have no value by default.
type: new GraphQL.GraphQLNonNull(menuType)
becomes
type: menuType

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