testing sails/mysql with fixtures primary key issue - sails.js

I have a sails app working against a legacy database (MySQL) and I would like to perform integration tests. I am using fixtures to load data into a separate test database using barrels. When I run my tests I get an error:
[Error (E_UNKNOWN) Encountered an unexpected error] Details: Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
Interview.js api/models/Interview.js:
module.exports = {
tableName: 'interview',
attributes: {
interviewId: {
type: 'integer',
columnName: 'interview_id',
primaryKey: true,
unique: true
},
...
}
};
interview.json tests/fixtures:
[
{
"interviewId": 1,
"title": "some string",
"createdDate": "2015-11-23T09:09:03.000Z",
"lastModified": "2015-11-23T09:09:03.000Z"
},
{...}
]
test environment config/env/test.js :
models: {
connection: 'test',
migrate: 'drop',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
}
The problem seems to lie in defining a primary key in the schema rather than letting sails create one automatically. If I remove the interviewId field from the model and set autoPK: true it works. But this does not accurately represent my data structure.
App info:
sails#0.11.2 sails-mysql#0.11.2 waterline#0.10.27 barrels#1.6.2 node v0.12.7
Many thanks,
Andy

Related

Mongoose 6 - How to get only the custom validation error message without the error?

const userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: [true, 'Email address is required'],
minlength: [4, 'Must be at least 4 characters long']
}
});
When I log the error I get:
User validation failed: email: Must be at least 4 characters long
How can I get only my custom message:
Must be at least 4 characters long
The only thing I do as of now is
const messageString = message.split(':');
messageString[3]
But is there a better way? I saw some solutions for this but its all for mongoose version 5 or less and it doesnt apply to version 6 as the error object has different properties.
On a side note (maybe this should be a seperate question) but as the error object is different now, how can I check if its a mongoose validation error? The err object doesnt come with it anymore.
Error: User validation failed: email: Must be at least 4 characters long
at ValidationError.inspect (/Users/... {
errors: {
email: ValidatorError: Must be at least 4 characters long
at validate (/Users/user/....
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
properties: [Object],
kind: 'minlength',
path: 'email',
value: 'hgg',
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: 'User validation failed'
}
Also, I dont want to have to access the modal property 'email' when accessing any other property as in my errorhandler, I want to keep it reusable for other errors too

Does Gatsby support MongoDB relationships?

I am building a personal blog and chose Gatsby because of the obvious reasons(performance and easy to start out) and because I have some React background for the frontend. Also, I had built a simple app to create my content(html string) and store in a MongoDB database using express server. Now for the blog, I am just trying to pull the data from MongoDB using gatsby-source-mongodb plugin.
My MongoDB schemas have relationships. For instance, a 'Post' schema has a 'user' property which is an ObjectID that references a user from 'User' schema. My config for the gatsby-source-mongodb looks like:
{
resolve: 'gatsby-source-mongodb',
options: {
dbName: 'KathaDB',
collection: 'posts',
server: {
address: "somecluster",
port: 27017
},
auth: {
user: 'someuser',
password: 'somepasswd'
},
extraParams: {
replicaSet: 'test',
ssl: true,
authSource: 'admin',
retryWrites: true,
preserveObjectIds: true
}
}
}
I have a couple of questions:
When I query, I get all the properties from my 'Post' schema but I don't have 'user' property in the response. I don't know if it is due to the type of the property. I dug up a bit and found a similar issue here. It seems like they have solved the issue by preserving the ObjectID but i didn't even get the property that is of type ObjectID.
Another thing, does this plugin support relationships? For example, is it possible to get the 'user' data when its ObjectID is given?
It does.
MongoDB relies on ObjectIDs for relationships, so you might have to add preserveObjectIds: true to your plugin options:
{
resolve: "gatsby-source-mongodb",
options: {
dbName: "KathaDB",
collection: "posts",
server: {
address: "somecluster",
port: 27017,
},
auth: {
user: "someuser",
password: "somepasswd",
},
extraParams: {
replicaSet: "test",
ssl: true,
authSource: "admin",
retryWrites: true,
preserveObjectIds: true,
},
preserveObjectIds: true, // <= here
},
};
I'm unsure whether gatsby-source-mongodb creates the relationships out of the box (I don't think it does, if my memory is correct), but with the ObjectIds, you can create foreign-key relationships using GraphQL.
There are two ways of doing this in Gatsby:
Using mappings in gatsby-config.js
Using a GraphQL #link directive through Gatsby's schema customization (from v2.2)
I recommend the second option, since it's a more GraphQL way of doing things, and happens in gatsby-node.js where most node operations are taking place. However, if you're starting out with Gatsby and GraphQL, the first option might be easier to set up.

using sequelize-cli db:seed, schema is ignored when accessing Postgres

i am building a web service using express.js and Sequilize with a Postgres DB.
Database holds a table 'country' under schema 'schema1'. Table 'country' has fields 'name', 'isoCode'.
Created a seed file to insert a list of countries inside table 'country'.
Seed file looks like :
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.bulkInsert(
'country',
[
{
"name":"Afghanistan",
"isoCode":"AF"
},
{
"name":"Åland Islands",
"isoCode":"AX"
},
{
"name":"Albania",
"isoCode":"AL"
},
{
"name":"Algeria",
"isoCode":"DZ"
},
{
"name":"American Samoa",
"isoCode":"AS"
},
{
"name":"Andorra",
"isoCode":"AD"
}
],
{
schema : 'schema1'
}
);
},
down: function (queryInterface, Sequelize) {
}
};
While running seed i get this error :
node_modules/sequelize-cli/bin/sequelize --url postgres://user:password#localhost:5432/database db:seed
Sequelize [Node: 0.12.6, CLI: 2.0.0, ORM: 3.11.0, pg: ^4.4.2]
Parsed url postgres://user:*****#localhost:5432/database
Starting 'db:seed'...
Finished 'db:seed' after 165 ms
== 20151029161319-Countries: migrating =======
Unhandled rejection SequelizeDatabaseError: relation "country" does not exist
at Query.formatError (node_modules/sequelize/lib/dialects/postgres/query.js:437:14)
at null.<anonymous> (node_modules/sequelize/lib/dialects/postgres/query.js:112:19)
at emit (events.js:107:17)
at Query.handleError (node_modules/pg/lib/query.js:108:8)
at null.<anonymous> (node_modules/pg/lib/client.js:171:26)
at emit (events.js:107:17)
at Socket.<anonymous> (node_modules/pg/lib/connection.js:109:12)
at Socket.emit (events.js:107:17)
at readableAddChunk (_stream_readable.js:163:16)
at Socket.Readable.push (_stream_readable.js:126:10)
at TCP.onread (net.js:538:20)
I think i am stuck on this. I would appreciate any provided help / guidance etc.
Thank you for your time.
I executed SQL query on Postgres :
ALTER ROLE <username> SET search_path TO schema1,public;
as noted here : Permanently Set Postgresql Schema Path
Then, executed seeder again succesfully :
node_modules/sequelize-cli/bin/sequelize --url postgres://user:password#localhost:5432/database db:seed
Sequelize [Node: 0.12.6, CLI: 2.0.0, ORM: 3.11.0, pg: ^4.4.2]
Parsed url postgres://user:*****#localhost:5432/database
Using gulpfile node_modules/sequelize-cli/lib/gulpfile.js
Starting 'db:seed'...
Finished 'db:seed' after 558 ms
== 20151029161319-Countries: migrating =======
== 20151029161319-Countries: migrated (0.294s)
Thanks #a_horse_with_no_name for the information about search_path. I wish the sequelize library could handle this situation, or maybe i misuse it.
update :
Opened a ticket on Github (https://github.com/sequelize/sequelize/issues/4778#issuecomment-152566806) and the solution is quite simple :
instead of setting only the table as the first argument, set
{tableName: 'country', schema : 'schema1'}
You can actually specify the schema and table name via object like is explained in this Github issue:
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.bulkInsert(
{ tableName: 'account', schema: 'crm' },
{
name: 'Michael'
},
{}
);
},
down: function (queryInterface, Sequelize) {
return queryInterface.bulkDelete({ tableName: 'account', schema: 'crm' }, null, {});
}
};

"email" validation rule crash sails server - Mongo with Sails.js

while the email validation rule fails on module of the sails.js, the server is crashing.
Here the snippet of my module:
// The user's email address
email: {
type: 'string',
email: true,
required: true,
unique: true
},
And the error as below :
err: Error (E_VALIDATION) :: 1 attribute is invalid
at WLValidationError.WLError (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\error\WLError.js:26:15)
at new WLValidationError (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\error\WLValidationError.js:20:28)
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\query\validate.js:45:43
at allValidationsChecked (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\core\validations.js:203:5)
at done (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:135:19)
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:32:16
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\core\validations.js:184:23
at done (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:135:19)
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:32:16
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\core\validations.js:157:64
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:125:13
at Array.forEach (native)
at _each (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:46:24)
at Object.async.each (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:124:9)
at validate (C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\waterline\lib\waterline\core\validations.js:156:11)
at C:\Users\yuri\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:125:13
Invalid attributes sent to User:
• email
• undefined should be a email (instead of "admin#gmailasd", which is a string)
The correct way to declare an email field is like this :
email: {
type: 'email',
required: true,//Email field will be required for insert or update
unique: true //Insert or update will crash if you try to insert duplicate email
},
You can see all different attribut types here http://sailsjs.org/documentation/concepts/models-and-orm/attributes
If you want to catch insert/update errors you can do this on your controller :
MyModel.create({email:email}).exec(function(err, model)
{
if(err)
{
//Check if it's a validation error or a crash
if(err.code == "E_VALIDATION")
sails.log.debug("valid fail, check form");
else
sails.log.debug("crash");
}
else
{
//do what you want to do with the data
}
});
Her the answer.
Thanks to jaumard, i found the problem.
I used undefined field in error, without checking if exists before
err.originalError.code but it was undefined.
So the correct way is :
err.originalError && err.originalError.code && err.originalError.code === 11000
and not
err.originalError.code === 11000.
Previous versions of Sails recommended that email validation was achieved like this
email: {
type: 'string',
email: true,
required: true
},
The current version should be like this
email: {
type: 'email',
required: true
},

TDD of Sailsjs Waterline Models with Vowsjs

My problem is trying to do TDD of Waterline models. The tests I present are just boilerplate to get my suite constructed. Nevertheless, they raise valid issues. The primary problem is that I require the model in the Vows.js test. In the test scope the model is defined, but it does not have any properties inherited from the Waterline package. For example, here is some model code for "EducationLevel":
module.exports = {
migrate: 'safe',
tableName: 'education_levels',
attributes: {
id : { type: 'integer', required: true },
description: { type: 'string', required: true },
display_sort: { type: 'integer', required: true}
}
};
And here are some trial tests:
vows = require('vows')
assert = require('assert')
EducationLevel = require('../api/models/EducationLevel')
vows.describe('tac_models').addBatch({
'EducationLevel model' : {
topic: function(){
educationLevel = EducationLevel.create();
return true;
},
'It exists': function (topic) {
assert.equal(EducationLevel.create,undefined);
assert.equal(EducationLevel.migrate,undefined);
}
}
}).export(module)
When I run the test, the first assertion passed, but the second does not:
vows spec/*
✗
EducationLevel model
✗ It exists
» expected undefined,
got 'safe' (==) // tac_models.js:13
✗ Broken » 1 broken (1.545s)
This shows that the test knows only what is explicitly declared in the EducationLevel definition. The 'migrate' property is defined because I explicitly define it in the code. It does not know about the Waterline method 'create'. How can I remedy this in a way that makes conventional TDD practical?