I have this code where I create a mongoose transaction. However it's not working as I expect. As you can see here, I'm testing the transaction by throwing an error before it can complete. However, for some reason, the account is always persisted rather than rolling back the transaction. Why
export const signUp = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
const createdAt = req.body.createdAt ? new Date(req.body.createdAt) : new Date()
const session = await mongoose.startSession()
try {
session.startTransaction()
const account: IAccount = await Account.create<ICreateAccountInput>(
[
{
createdAt,
accountName: req.body.accountName,
accountOwnerName: req.body.accountOwnerName,
accountOwnerEmail: req.body?.accountOwnerEmail,
contactName: req.body?.contactName || undefined,
contactNumber: req.body.contactNumber || undefined,
contactEmail: req.body.contactEmail || undefined,
},
],
{ session },
)[0]
// throw error to test transaction cancellation
throw new Error('Test error')
const accountAdmin: IUser = await User.create<ICreateUserInput>(
[
{
createdAt,
accountId: account._id,
username: req.body.accountOwnerName,
email: req.body.accountOwnerEmail,
role: UserRoles.AccountAdmin,
password: req.body.password,
passwordConfirm: req.body.passwordConfirm,
},
],
{ session },
)[0]
await session.commitTransaction()
createSendToken(accountAdmin, 201, res, account)
} catch (e) {
session.abortTransaction()
throw e
} finally {
session.endSession()
}
})
Here is my catchAsync function in case this is the issue. It just catches errors so I don't have to write try/catch blocks everywhere:
export const catchAsync = fn => {
return (req, res, next) => {
fn(req, res, next).catch(next)
}
}
That code creates a session and transaction, but the account create is not using that session, so it is not within the transaction.
To correct this, wrap the object passed to create in an array and add the session option to the call:
Account.create<ICreateAccountInput>([{
createdAt,
accountName: req.body.accountName,
accountOwnerName: req.body.accountOwnerName,
accountOwnerEmail: req.body?.accountOwnerEmail,
contactName: req.body?.contactName || undefined,
contactNumber: req.body.contactNumber || undefined,
contactEmail: req.body.contactEmail || undefined,
}],
{session: session}
)
I tried to run the created migration, but got an unknown error. I'm new to this type of work with databases. Maybe someone has encountered this problem? Script for migration: npx knex migrate:latest --knexfile ./app/database/knexfile.js. Maybe it has to do with the knex configuration, the paths specified in the configuration are correct. Seeeds are not used yet.
knexfile:
require('dotenv').config();
const db_user = process.env.DB_USER;
const db_password = process.env.DB_PASSWORD;
const db_name = process.env.DB_NAME;
/**
* #type { Object.<string, import("knex").Knex.Config> }
*/
module.exports = {
development: {
client: 'postgresql',
connection: {
database: `${db_name}`,
user: `${db_user}`,
password: `${db_password}`,
},
pool: {
min: 2,
max: 10,
},
migrations: {
tableName: 'knex_migrations',
directory: './migrations',
},
seeds: {
directory: './seeds',
},
},
};
Migration file:
// eslint-disable-next-line node/exports-style
exports.up = (knex) => {
return knex.schema
.createTable('users', (table) => {
table.uuid('id').primary();
table.string('name');
table.string('email');
table.sting('password');
})
.createTable('watchLists', (table) => {
table.uuid('id').primary();
table
.foreign('userId')
.references('id')
.inTable('users')
.onDelete('CASCADE');
table.string('name');
})
.createTable('films', (table) => {
table.uuid('id').primary();
table.string('name');
table.string('rate');
})
.createTable('addedFilms', (table) => {
table.uuid('id').primary();
table
.foreign('watchListId')
.references('id')
.inTable('watchList')
.onDelete('CASCADE');
table
.foreign('filmId')
.references('id')
.inTable('films')
.onDelete('CASCADE');
});
};
// eslint-disable-next-line node/exports-style
exports.down = (knex) => {
return knex.schema
.dropTableIfExists('addedFilms')
.dropTableIfExists('films')
.dropTableIfExists('users')
.dropTableIfExists('watchList');
};
Error:
������������ "undefined" �� ������ �������� ����������� (�� ������)
error: ������������ "undefined" �� ������ �������� ����������� (�� ������)
at Parser.parseErrorMessage (D:\All-Pet-Projects\effective-app\evgeny-trubish-node\node_modules\pg-protocol\dist\parser.js:287:98)
at Parser.handlePacket (D:\All-Pet-Projects\effective-app\evgeny-trubish-node\node_modules\pg-protocol\dist\parser.js:126:29)
at Parser.parse (D:\All-Pet-Projects\effective-app\evgeny-trubish-node\node_modules\pg-protocol\dist\parser.js:39:38)
at Socket.<anonymous> (D:\All-Pet-Projects\effective-app\evgeny-trubish-node\node_modules\pg-protocol\dist\index.js:11:42)
at Socket.emit (node:events:390:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
I'm sending data to server using fetch:
const post = async(data)=>{
console.log(data)
const response = await fetch("/comments", {
method: "POST",
body:JSON.stringify(data)
});
return response.json();
};
data is console logged, it's a standard object with keys and values. And it arrives to server, same keys and values.
On the server I run this express-validation for sanitizing:
router.use(express.json());
router.post("/",
body("email").isEmail().normalizeEmail(),
body("name").trim().escape(),
body("msg").not().isEmpty().trim().escape(),
(req,res,next)=>{
const errors = validationResult(req);
console.log(errors);
if (!errors.isEmpty()) { res.status(422).json({ errors: errors.array() }); return};
try{
saveComment(req.body, (err,doc) => {
err? next(createError(500, "Couldn't save the document. Try again.")):
res.json({msg:"saved"});
});
} catch(e) {
next(createError(500, ISE));
}
});
And what I get is:
Result {
formatter: [Function: formatter],
errors: [
{
value: undefined,
msg: 'Invalid value',
param: 'email',
location: 'body'
},
{
value: undefined,
msg: 'Invalid value',
param: 'msg',
location: 'body'
}
]
}
POST /comments 422 17.821 ms - 126
What is the error?
Set header in the fetch function:
headers: {"Content-Type":"application/json"}
I'm working on a Ionic 2 (2.0.0-rc.2) app with a sqlite storage and i've ran into some problems, hence i'm seeking for help here.
When starting the app for the first time i'm creating the database(tables) insert a few values. So far everything goes alright. However when I try to retrieve data and read it from the database i'm getting an error.
The class were i'm talking about:
import {Component} from '#angular/core';
import {Platform} from 'ionic-angular';
import {StatusBar, Splashscreen, SQLite} from 'ionic-native';
import {HomePage} from '../pages/home/home';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
users = [];
rootPage = HomePage;
constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
let db = new SQLite();
console.log("open database app.compontent")
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
db.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {}).then((data) => {
console.log("USER TABLE CREATED: ", data);
}, (error) => {
console.error("USER Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {}).then((data) => {
console.log("MELDING TABLE CREATED: ", data);
}, (error) => {
console.error("MELDING Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS kasteelOnderdelen (idkasteelOnderdelen INTEGER PRIMARY KEY AUTOINCREMENT, onderdeelNaam TEXT, onderdeelShort TEXT, onderdeelHealth TEXT); ", {}).then((data) => {
console.log("KASTEELONDERDELEN TABLE CREATED: ", data);
}, (error) => {
console.error("KASTEELONDERDELEN Unable to execute sql", error);
});
db.executeSql("CREATE TABLE IF NOT EXISTS user_has_kasteelOnderdelen (id INTEGER PRIMARY KEY AUTOINCREMENT, user_iduser INTEGER, kasteelOnderdelen_idkasteelOnderdelen INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser), FOREIGN KEY (kasteelOnderdelen_idkasteelOnderdelen) REFERENCES kasteelOnderdelen(idkasteelOnderdelen));", {}).then((data) => {
console.log("USER_HAS_KASTEELONDERDELEN TABLES CREATED: ", data);
}, (error) => {
console.error("USER_HAS_KASTEELONDERDELEN Unable to execute sql", error);
});
console.log('Creating database complete!');
db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']).then((data) => {
console.log("Inserted row 1: ", data);
}, (error) => {
console.error("Unable to insert row 1: ", error);
});
db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']).then((data) => {
console.log("Inserted row 2: ", data);
}, (error) => {
console.error("Unable to insert row 2: ", error);
});
db.executeSql('SELECT * FROM user', []).then((resultSet) => {
if(resultSet.res.rows.length > 0) {
this.users = [];
for(let i = 0; i < resultSet.res.rows.length; i++) {
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
}
}, (error) => {
console.log(error);
});
}, (error) => {
console.error("Unable to open database", error);
});
Splashscreen.hide();
});
}
}
error (when I run it on a device (Nexus 5):
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 12 : Error: Uncaught (in promise): TypeError: Cannot read property 'rows' of undefined
at s (file:///android_asset/www/build/polyfills.js:3:8568)
at file:///android_asset/www/build/polyfills.js:3:8940
at t.invokeTask (file:///android_asset/www/build/polyfills.js:3:14051)
at Object.inner.inner.fork.onInvokeTask (file:///android_asset/www/build/main.js:3:22982)
at t.invokeTask (file:///android_asset/www/build/polyfills.js:3:13987)
at e.runTask (file:///android_asset/www/build/polyfills.js:3:11411)
at i (file:///android_asset/www/build/polyfills.js:3:8028)
at u (file:///android_asset/www/build/polyfills.js:2:16275)
at file:///android_asset/www/build/polyfills.js:2:16399
at MutationObserver.r (file:///android_asset/www/build/polyfills.js:2:14841)
Additional findings/notes:
If i write console.log(resultSet);
The return will be [object object]
For now it's all in the same class and on the longrun it will be in it's own StorageProvider/Service but i've moved it all here to debug it. (without success so far)
Does someone have a clue what causes the error?
Thanks in advance.
Poerkie
UPDATE 20-12 (after implementing tips and suggestions):
current code:
import {Injectable} from '#angular/core';
import 'rxjs/add/operator/map';
import {SQLite} from 'ionic-native';
import {Platform} from 'ionic-angular';
#Injectable()
export class StorageService {
public database: SQLite;
public users;
constructor(private platform: Platform) {
console.log(' StorageService Initialised');
this.platform.ready().then(() => {
this.database = new SQLite();
this.database.openDatabase({name: "data.db", location: "default"}).then(() => {
console.log("SUCCES Opening DB storageService");
let s1 = this.createUserTable()
let s2 = this.database.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {});
return Promise.all([s1, s2]).then(() => {
console.log('creating database complete!');
let i1 = this.insertUser();
let i2 = this.database.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']);
return Promise.all([i1, i2]).then((res) => {
console.log('all insert querys executed', res);
}).then(() => {
console.log('executing sql statement');
return this.database.executeSql('SELECT * FROM user', []);
}).then((resultSet) => {
console.log('after then((resultset): ' + resultSet);
console.log(JSON.stringify(resultSet, undefined, 2));
if (resultSet.res.rows.length > 0) {
console.log('if statement true');
this.users = [];
for (let i = 0; i < resultSet.res.rows.length; i++) {
console.log('forloop');
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
console.log('after forloop');
}
}).catch((error) => {
console.error("db error", error);
})
})
}, (error) => {
console.log("ERROR oepening DB storageService: ", error);
});
});
}
public createUserTable() {
this.database.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {}).then((data) => {
console.log("USER TABLE CREATED: ", data);
}, (error) => {
console.error("USER Unable to execute sql", error);
});
}
public insertUser() {
this.database.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']).then((data) => {
console.log('Insert Succesful');
}, (error) => {
console.error('user not inserted: ' + error);
});
}
}
nexus 5 outputlog:
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : StorageService Initialised
I/chromium: [INFO:CONSOLE(6)] " StorageService Initialised", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : CastleService initialized
I/chromium: [INFO:CONSOLE(6)] "CastleService initialized", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 5 : SlimLabsService initialized
I/chromium: [INFO:CONSOLE(5)] "SlimLabsService initialized", source: file:///android_asset/www/build/main.js (5)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 5 : Logging in!
I/chromium: [INFO:CONSOLE(5)] "Logging in!", source: file:///android_asset/www/build/main.js (5)
12-20 07:48:04.042 796-18483/? W/InputMethodManagerService: Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy#fd827af attribute=null, token = android.os.BinderProxy#7d616de
D/CordovaWebViewImpl: onPageFinished(file:///android_asset/www/index.html)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 19 : DEVICE READY FIRED AFTER
I/chromium: [INFO:CONSOLE(19)] "DEVICE READY FIRED AFTER", source: file:///android_asset/www/build/main.js (19)
I/App: WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!
D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js: Line 175 : OPEN database: data.db
I/chromium: [INFO:CONSOLE(175)] "OPEN database: data.db", source: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js (175)
V/SQLitePlugin: Android db implementation: built-in android.database.sqlite package
V/info: Open sqlite db: /data/user/0/com.ionicframework.cwistlev1105374/databases/data.db
D/SystemWebChromeClient: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js: Line 179 : OPEN database: data.db - OK
I/chromium: [INFO:CONSOLE(179)] "OPEN database: data.db - OK", source: file:///android_asset/www/plugins/cordova-sqlite-storage/www/SQLitePlugin.js (179)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : SUCCES Opening DB storageService
I/chromium: [INFO:CONSOLE(6)] "SUCCES Opening DB storageService", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : USER TABLE CREATED:
I/chromium: [INFO:CONSOLE(6)] "USER TABLE CREATED: ", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : creating database complete!
I/chromium: [INFO:CONSOLE(6)] "creating database complete!", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : Insert Succesful
I/chromium: [INFO:CONSOLE(6)] "Insert Succesful", source: file:///android_asset/www/build/main.js (6)
12-20 07:48:04.629 1596-1596/? I/Keyboard.Facilitator: onFinishInput()
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : all insert querys executed
I/chromium: [INFO:CONSOLE(6)] "all insert querys executed", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : executing sql statement
I/chromium: [INFO:CONSOLE(6)] "executing sql statement", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : after then((resultset): [object Object]
I/chromium: [INFO:CONSOLE(6)] "after then((resultset): [object Object]", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : {
"rows": {
"length": 42
},
"rowsAffected": 0
}
I/chromium: [INFO:CONSOLE(6)] "{
"rows": {
"length": 42
},
"rowsAffected": 0
}", source: file:///android_asset/www/build/main.js (6)
D/SystemWebChromeClient: file:///android_asset/www/build/main.js: Line 6 : db error
I/chromium: [INFO:CONSOLE(6)] "db error", source: file:///android_asset/www/build/main.js (6)
additional information:
There isn't more information about the error or I don't know how to retrieve that. (not getting a stack trace or anything);
Why doesn't it want to pass on the results! into the users[]?
I agree with Suraj Rao about the async result. I would add that you could do all the create tables in a Promise.all(). And when in between 'then' statement, place a 'return' on the query which result is expected in the next "then". Eg:
query.then( (res) => { return query2 }
).then( (res2) => { do something }
).catch( (err) => {...});
In your case:
let db = new SQLite();
console.log("open database app.compontent")
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
let s1 = db.executeSql("CREATE TABLE IF NOT EXISTS user (iduser INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT, castleType TEXT, castleHealth INTEGER); ", {});
let s2 = db.executeSql("CREATE TABLE IF NOT EXISTS melding (idmelding INTEGER PRIMARY KEY AUTOINCREMENT, meldingType TEXT, meldingTitel TEXT, meldingOmschrijving TEXT, ruimte INTEGER, user_iduser INTEGER, FOREIGN KEY (user_iduser) REFERENCES user(iduser)); ", {})
let sN = db.executeSql("CREATE TABLE ....);
return Promise.all([s1,s2,....,sN]);
).then(
() => {
console.log('Creating database complete!');
let i1 = db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Jan', 'Klaassen', 'mannelijk', '10']);
let i2 = db.executeSql("INSERT INTO user (firstname, lastname, castletype, castleHealth) VALUES (?, ?, ?, ?)", ['Klaas', 'Janssen', 'mannelijk', '5']);
let iN = db.executeSql("INSERT INTO ....");
return Promise.all([i1,i2,....,iN]);
).then(
(res) => {
console.log('all insert over ' , res)
}
).then(
() => {return db.executeSql('SELECT * FROM user', []);}
).then(
(resultSet) => {
if(resultSet.res.rows.length > 0) {
this.users = [];
for(let i = 0; i < resultSet.res.rows.length; i++) {
this.users.push({
"iduser": resultSet.res.rows.item(i).iduser,
"firstname": resultSet.res.rows.item(i).firstname,
"lastname": resultSet.res.rows.item(i).lastname,
});
}
}
}
).catch(
(error) => {
console.error("db error", error);
}
);
The db related functions you are using return a promise.
The then() is asynchronous meaning all your db functions from create,insert,select may execute before one or more of the then method is called.
Suppose you have only user table,You will need to
db.executeSql(`
CREATE TABLE IF NOT EXISTS user (
iduser INTEGER PRIMARY KEY AUTOINCREMENT,
firstname TEXT,
lastname TEXT,
castleType TEXT,
castleHealth INTEGER
); `, {})
.then((data) => {
console.log("USER TABLE CREATED: ", data);
db.executeSql(`
INSERT INTO user (
firstname,
lastname,
castletype,
castleHealth
) VALUES (?, ?, ?, ?)`,
['Jan', 'Klaassen', 'mannelijk', '10'])
.then((data) => {
//select here
console.log("Inserted row 1: ", data);
}, (error) => {
console.error("Unable to insert row 1: ", error);
});
}, (error) => {
console.error("USER Unable to execute sql", error);
});
and so on.
Your resultset.res is probably null because your table is not created yet.
Also to print objects do : console.log(JSON.stringify(resultSet,undefined,2));
Hope it will work for you, refer the below simple implementation.
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
#Component({
selector: 'page-about',
templateUrl: 'about.html',
providers:[SQLite]
})
export class AboutPage {
rows: any;
data: any;
public people: Array<Object>;
getdata: string;
constructor(public navCtrl: NavController, private sqlite: SQLite) {
this.sqlite.create({
name: 'data.db',
location: 'default'
}).then((db: SQLiteObject) => {
db.executeSql('CREATE TABLE IF NOT EXISTS supriya(name VARCHAR(32))', {})
.then(() => console.log('Executed SQL'))
.catch(e => console.log(e));
db.executeSql('INSERT INTO supriya VALUES (?)', ['Supriya']).then(() => console.log('value inserted in SQL'))
.catch(e => console.log(e));
db.executeSql('select * from supriya', []).then(data => {
console.log("select statment executed"+ data);
this.people = [];
if(data.rows.length > 0) {
for(var i = 0; i < data.rows.length; i++) {
this.people.push({name: data.rows.item(i).name});
console.log(data.rows.item(i).name);
}
}
}, (error) => {
console.log("ERROR: " + JSON.stringify(error));
})
})
}
}
<ion-list>
<ion-item *ngFor="let person of people">
{{person.name}}
</ion-item>
</ion-list>