How can I initialize my database in Sails.js? I want to parse some big XML files and insert them into the database, so that my Sails-API can handle the requests.
I would like to insert the data via Waterline and not with the REST API. Is there an opportunity to create management commands (like in Django), so I can do something like sails init-database which would run my script that parses the XML and inserts it directly via the Waterline model classes?
I can't use bootstrap.js for this kind of task, because I don't want to create the database contents every time sails is lifted. Is there a command line argument, where I can disable running the bootstrap.js?
module.exports.bootstrap = function (cb) {
Model.count().exec(function (err, count) {
if (err) return cb(err);
if (count > 0) return cb();
Model.create(initdata).exec(cb);
});
};
You can use bootstrap.js for that purpose, simply add some logic in it to test if your XML datas where already inserted. with something like that
module.exports.bootstrap = function(cb) {
MyModelDefinedInXML.find({Afield : "A value I know"}).exec(function(err,res){
if(err){ //manage it ;}
if(res.length>0){
//skip
cb();
}else{
//perform insert
cb();
}
});
}
For dev you simply don't set sails.config.models.migrate so if you want to reset everything you can choose option "3" in order to wipe/drop everything in your DB.
Related
Im still new in coding and struggle on some basics.
I have to create a URL-Shortener with Express and MongoDB, additionally I use React for the frontent.
I have an input field and a submit button. The inputed URL will be sended by a POST request to the server.
(a console.log shows that this works fine)
Using "npm short-id"-package helps me to define an id for the shortUrl if the inputed url is accepted by "npm validid"- package.
Up to here, everything is fine.
Now, the "amateur-struggle" begins :)
I have learned the common pattern to save data in the mongoDB, example:
var createAndSaveUrl = function(done) {
var NEEDHELP = new Url({url: req.body.url, shortUrl: shortid.generate()});
NEEDHELP.save(function(err, data) {
if (err) return console.error(err);
done(null, data)
});
};
The "NEEDHELP" variable makes me go crazy, because I think I have to give it a unique name for the DB but dont know how to do that.
Someone can help me out?
var createAndSaveUrl = function(done) { var shortenedUrl= new Url({url: req.body.url, shortUrl: shortid.generate()});
shortenedUrl.save(function(err, data) {
if (err) return console.error(err);
done(null, data) }); };
I think this is enough.
I am using sails js with it sails-mssqlserver adapter. The problem with it is that if my stored procedure returns multiple result sets then I only receive one result set which is the latest of all.
The same stored procedure is working fine with Java and I get to iterate over the relevant result sets.
I need to know if there is some specific way to access all result sets in sails-mssqlserver?
The sails-mssqlserver adapter is a wrapper of the official Microsoft SQL Server client for Node.js available here its dependecy however is not on the latest release.
Option 1:
As per this official documentation of the MsSQL package, you can enable multiple recordsets in queries with the request.multiple = true command.
To enable multiple queries/recordsets in the sails-mssqlserver adapter, a hackish workaround is to open sails-mssqlserver/lib/adapter.js and edit the raw query function. Adding request.multiple = true below var request = new mssql.Request(mssqlConnect). As shown in the example below.
// Raw Query Interface
query: function (connection, collection, query, data, cb) {
if (_.isFunction(data)) {
if (debugging) {
console.log('Data is function. A cb was passed back')
}
cb = data
data = null
}
adapter.connectConnection(connection, function __FIND__ (err, uniqId) {
if (err) {
console.error('Error inside query __FIND__', err)
return cb(err)
}
uniqId = uniqId || false
var mssqlConnect
if (!uniqId) {
mssqlConnect = connections[connection].mssqlConnection
} else {
mssqlConnect = connections[connection].mssqlConnection[uniqId]
}
var request = new mssql.Request(mssqlConnect)
// Add it here
request.multiple = true
request.query(query, function (err, recordset) {
if (err) return cb(err)
if (connections[connection] && !connections[connection].persistent) {
mssqlConnect && mssqlConnect.close()
}
cb(null, recordset)
})
})
},
Now the returned recordset should contain multiple results.
Option 2:
A more sustainable option for use cases where running a stored procedure which returns multiple recordsets, is to use the latest version of the official Microsoft SQL Server client for Node.js. Information on running stored procedures is available here
First install the latest package:
npm install mssql --save
In your code where you would like to run the stored procedure add a connection to the mssql database:
// require the mssql package
const sql = require('mssql')
// make a connection, you can use the values you have already stored in your adapter
const pool = new sql.ConnectionPool({
user: sails.config.connections.<yourMsSQLConnection>.user,
password: sails.config.connections.<yourMsSQLConnection>.password,
server: sails.config.connections.<yourMsSQLConnection>.server,
database: sails.config.connections.<yourMsSQLConnection>.database
})
// connect the pool and test for error
pool.connect(err => {
// ...
})
// run the stored procedure using request
const request = new sql.Request()
request.execute('procedure_name', (err, result) => {
// ... error checks
console.log(result.recordsets.length) // count of recordsets returned by the procedure
console.log(result.recordsets[0].length) // count of rows contained in first recordset
console.log(result.recordset) // first recordset from result.recordsets
console.log(result.returnValue) // procedure return value
console.log(result.output) // key/value collection of output values
console.log(result.rowsAffected) // array of numbers, each number represents the number of rows affected by executed statemens
// ...
})
// you can close the pool using
pool.close()
In cases, where the sails-* database adapter doesn't include all the functionality you require. I find it best to create a sails Service that wraps the additional functionality. It is a really clean solution.
I'm trying to export some data into a CSV file from a MySQL database using Meter/Sequelize. What I've done so far is to create a Meteor method called by the client which then call a server side function that return the data and I parse it into a csv string. My issue is returning the date client-side.
What I did
I have my CSV String server-side and I'm using FileSaver.js which can only be used client-side.
My "solution" was to create a client-side collection in which I published the String.
methods.js
run({exportParam}) {
if (!this.isSimulation) {
query.booksQuery(exportParam.sorted, exportParam.filtered, 0).then(
result => {
let CSVArr = [];
result.rows.forEach((value) => {
CSVArr.push(value.dataValues);
});
const CSVString = Baby.unparse(CSVArr,{ delimiter: ";"});<-CSV String
console.log("CSVString : ", CSVString);
Meteor.publish("CSVString", function() { <= publication
this.added("CSVCollection", Random.id(), {CSVString: CSVString});
this.ready();
});
});
}
},
And on the client-side I subscribe to the publication this way :
ExportButton.jsx
const handle = Meteor.subscribe('CSVString', {}, function() {
const exportString = myTempCollection.findOne().CSVString;
const blob = new Blob([exportString], {type:"text/plain;charset=utf
8"});
FileSaver.saveAs(blob, "test.csv");
});
My issue
It works great the first time I click my button and a CSV file is downloaded. The problem is that if I do it again I get the same file as the first one and I get this message on my console.
Ignoring duplicate publish named 'CSVString'
I'm pretty sure the problem comes from the fact that every time I click the button the same "CSVString" publication is created.
I'd like to know to know if there is a solution to this problem or if my approach is wrong.
Please let me know if you need anything else.
You are correct in assuming that you are trying to publish to the same collection every time. I think you should only do the publish once, and do that separately from inserting a record into the collection.
I have been looking around for suitable ways to 'clean up' created records after tests are run using Protractor.
As an example, I have a test suite that currently runs tests on create and update screens, but there is currently no delete feature, however there is a delete end point I can hit against the backend API.
So the approach I have taken is to record the id of the created record so that in an afterAll I can then issue a request to perform a delete operation on the record.
For example:
beforeAll(function() {
loginView.login();
page.customerNav.click();
page.customerAddBtn.click();
page.createCustomer();
});
afterAll(function() {
helper.buildRequestOptions('DELETE', 'customers/'+createdCustomerId).then(function(options){
request(options, function(err, response){
if(response.statusCode === 200) {
console.log('Successfully deleted customer ID: '+ createdCustomerId);
loginView.logout();
} else {
console.log('A problem occurred when attempting to delete customer ID: '+ createdCustomerId);
console.log('status code - ' + response.statusCode);
console.log(err);
}
});
});
});
//it statements below...
Whilst this works, I am unsure whether this is a good or bad approach, and if the latter, what are the alternatives.
I'm doing this in order to prevent a whole load of dummy test records being added over time. I know you could just clear down the database between test runs, e.g. through a script or similar on say a CI server, but it's not something I\we have looked into further. Plus this approach seems on the face of it simpler, but again I am unsure about the practicalities of such an approach directly inside the test spec files.
Can anyone out there provide further comments\solutions?
Thanks
Well, for what it's worth I basically use that exact same approach. We have an endpoint that can reset data for a specific user based on ID, and I hit that in a beforeAll() block as well to reset the data to an expected state before every run (I could have done it afterAll as well, but sometimes people mess with the test accounts so I do beforeAll). So I simply grab the users ID and send the http request.
I can't really speak to the practicality of it, as it was simply a task that I accomplished and it worked perfectly for me so I saw no need for an alternative. Just wanted to let you know you are not alone in that approach :)
I'm curious if other people have alternative solutions.
The more robust solution is to mock your server with $httpBackend so you don't have to do actual calls to your API.
You can then configure server responses from your e2e test specs.
here's a fake server example :
angular.module('myModule')
.config(function($provide,$logProvider) {
$logProvider.debugEnabled(true);
})
.run(function($httpBackend,$log) {
var request = new RegExp('\/api\/route\\?some_query_param=([^&]*)');
$httpBackend.whenGET(request).respond(function(method, url, data) {
$log.debug(url);
// see http://stackoverflow.com/questions/24542465/angularjs-how-uri-components-are-encoded/29728653#29728653
function decode_param(param) {
return decodeURIComponent(param.
replace('#', '%40').
replace(':', '%3A').
replace('$', '%24').
replace(',', '%2C').
replace(';', '%3B').
replace('+', '%20'));
}
var params = url.match(request);
var some_query_param = decodeURIComponent(params[1]);
return [200,
{
someResponse...
}, {}];
});
});
Then load this script in your test environnement and your done.
I'm used to code using C# and sometimes I create transactions that have more than one call to different repositorys, and if one that calls fails, the rollback covers every repository.
The code is like this:
using (var scope = new TransactionScope())
{
itemRepository.deleteItems(items);
bookRepository.deleteBooks(books);
scope.Complete();
}
This ensures that if an error occurs on bookRepository, the deleted items will be on the rollback.
Now, I'm trying to do the same thing using node.js.
I found the mssql package that has the option to create transactions, but only inside the repository, like this:
var transaction = new sql.Transaction(/* [connection] */);
transaction.begin(function(err) {
// ... error checks
var request = new sql.Request(transaction);
request.query('insert into mytable (mycolumn) values (12345)', function(err, recordset) {
// ... error checks
transaction.commit(function(err, recordset) {
// ... error checks
console.log("Transaction committed.");
});
});
});
So, there's some way to create a transaction on the Service that covers more than one repository like I described?
you can create a transaction scope using a beautiful framework called Sequelize.
If you look the page relative to transactions, you can find a way to pass a transaction to all queries.
sequelize.transaction(function (t1) {
// With CLS enabled, the user will be created inside the transaction
return User.create({ name: 'Alice' });
});