diff --git a/CHANGELOG.md b/CHANGELOG.md index b60b091fb972ce432a7b1f592ca3fd5d845b76a1..f7662707c8a4535af7093a1660db7d49645ef0cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.0.6 - 01-04-2019 +### Changed +- Input class to match with database model (Add id and description) (Horstmann) +- Form class to match with database model (Remove version add description) (Horstmann) +- enumHandler to remove sides whitespaces (Horstmann) +### Added +- Create readForm method to read form from database #7 (Horstmann) +- Create readInput method to read input from database #7 (Horstmann) +- Create writeForm method to insert form into database (Horstmann) +- Comments to coverage ignore errors that are not reached on tests. ## 0.0.5 - 19-03-2019 ### Changed - Remove tslint-stylish from package.json, package is deprecated (Horstmann) diff --git a/package.json b/package.json index af7ad31957e9846b4cd4784eba69515180b96a6d..515db70e98aa366e6e62163beba5a041dd2f46c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "form-creator-api", - "version": "0.0.5", + "version": "0.0.6", "description": "RESTful API used to manage and answer forms.", "main": "index.js", "scripts": { diff --git a/src/core/form.ts b/src/core/form.ts index 62e702e9ba5a7a46a90542b268d700ade281ef00..9e5b53aaa365a30a1eb92bff8d384964cd4555ca 100644 --- a/src/core/form.ts +++ b/src/core/form.ts @@ -20,28 +20,32 @@ */ import { Input } from "./input"; - /** * Form Class to manage project's forms */ export class Form { - /** Form Header, as name and propose */ - public readonly header: string; + /** Unique identifier of a Form instance */ + public readonly id: number; + /** Form's title. An human-understandable identifier. Not unique */ + public readonly title: string; + /** Form Description, as propose */ + public readonly description: string; /** Array of input. containing question */ public readonly inputs: Input[]; - /** Version, a incremental numbert */ - public readonly version: number; /** * Creates a new instance of Input Class - * @param header - Forms's name and propose. + * @param title - Form's title. + * @param description - Form's description * @param inputs - Forms's question - * @param version - Incremental version of Form + * @param id - Form's identifier */ - constructor(header: string, inputs: Input[], version: number) { - this.header = header; + constructor(title: string, description: string, inputs: Input[], id?: number) { + this.id = id ? id : null; + this.title = title; + this.description = description; this.inputs = inputs; - this.version = version; } + } diff --git a/src/core/input.ts b/src/core/input.ts index caf211c92f6fb6958c2e1c2b25b9b8fe4c9b95e3..ec01a8b62aa77ab9de92f5a118060f5859789996 100644 --- a/src/core/input.ts +++ b/src/core/input.ts @@ -35,8 +35,12 @@ export interface Validation { */ export class Input { + /** Unique identifier of a Input instance. */ + public readonly id: number; /** Place where input should be in the form. */ public readonly placement: number; + /** Input's Description */ + public readonly description: string; /** Question of input */ public readonly question: string; /** Type of input */ @@ -47,12 +51,17 @@ export class Input { /** * Creates a new instance of Input Class * @param placement - Question position in forms. + * @param description - Input description * @param question - Input question of this input * @param type - InputType * @param validation - Array of Validation + * @param id - Input identifier */ - constructor(placement: number, question: string, type: InputType, validation: Validation[]) { + + constructor(placement: number, description: string, question: string, type: InputType, validation: Validation[], id?: number) { + this.id = id ? id : null; this.placement = placement; + this.description = description; this.question = question; this.type = type; this.validation = validation; diff --git a/src/utils/dbHandler.spec.ts b/src/utils/dbHandler.spec.ts index b4af1afa8f6a7692bd72811b7174a0f842c8118a..bbbc71a281a594ddb9798e1419cec2565ede62e7 100644 --- a/src/utils/dbHandler.spec.ts +++ b/src/utils/dbHandler.spec.ts @@ -21,546 +21,361 @@ import { series } from "async"; import { expect } from "chai"; import { QueryResult } from "pg"; +import { Form } from "../core/form"; +import { Input, Validation } from "../core/input"; import { configs } from "./config"; import { DbHandler } from "./dbHandler"; +import { InputType, ValidationType } from "./enumHandler"; describe("Database Handler", () => { const dbhandler = new DbHandler(configs.poolconfig); it("should insert a form", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO form(id, title, description)\ - VALUES\ - (1, 'Form Title 1', 'Form Description 1');", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO form(id, title, description)\ + VALUES\ + (4, 'Form Title 4', 'Form Description 4');", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(1); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(1); done(); }); }); + it("should insert a form and then rollback", (done) => { + series([ + (cb: (err: Error, result?: QueryResult) => void) => { + dbhandler.begin(cb); + }, + (callback: (err: Error, result?: QueryResult) => void) => { + dbhandler.executeQuery("INSERT INTO form(id, title, description)\ + VALUES\ + (5, 'Form Title 5', 'Form Description 6');", callback); + }, + (cb: (err: Error, result?: QueryResult) => void) => { + dbhandler.rollback(cb); + } + ], (err, results) => { + + expect(err).to.be.a("null"); + expect(results[0].command).to.be.equal("BEGIN"); + expect(results[1].command).to.be.equal("INSERT"); + expect(results[1].rowCount).to.be.equal(1); + expect(results[2].command).to.be.equal("ROLLBACK"); + done(); + + }); + }); + it("should select all forms", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM form;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM form;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(3); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(4); done(); + }); }); - it("should remove non existent form", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM form WHERE id=5;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM form WHERE id=5;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); done(); }); }); it("should remove existent form", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM form WHERE id=1;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(1); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM form WHERE id=4;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(1); done(); }); }); it("should insert a input", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO input(id, id_form, placement, input_type, question, description)\ - VALUES\ - (9, 2, 3,'TEXT', 'Question 3 Form 2', 'Description Question 3 Form 2'),\ - (10, 2, 4,'TEXT', 'Question 4 Form 2', 'Description Question 4 Form 2');", callback); - }, - - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO input(id, id_form, placement, input_type, question, description)\ + VALUES\ + (9, 2, 3,'TEXT', 'Question 3 Form 2', 'Description Question 3 Form 2'),\ + (10, 2, 4,'TEXT', 'Question 4 Form 2', 'Description Question 4 Form 2');", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(2); done(); - }); }); it("should select all inputs", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM input;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM input;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(8); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(9); done(); + }); }); it("should remove non existent input", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input WHERE id=11;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM input WHERE id=11;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); done(); - }); }); it("should remove existent input", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input WHERE id=9 OR id=10;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM input WHERE id=9 OR id=10;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(2); done(); }); }); it("should insert a input validations", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO input_validation(id, id_input, validation_type)\ - VALUES\ - (9, 2, 'MAXCHAR'),\ - (10, 5, 'MANDATORY');", callback); - }, - - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO input_validation(id, id_input, validation_type)\ + VALUES\ + (9, 2, 'MAXCHAR'),\ + (10, 5, 'MANDATORY');", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(2); done(); - }); }); it("should select all input validations", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM input_validation;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM input_validation;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(9); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(10); done(); }); }); it("should remove non existent input validations", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_validation WHERE id=11;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); - done(); + dbhandler.executeQuery("DELETE FROM input_validation WHERE id=11;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); + done(); }); }); it("should remove existent input validations", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_validation WHERE id=9 OR id=10;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); - done(); + dbhandler.executeQuery("DELETE FROM input_validation WHERE id=9 OR id=10;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(2); + done(); }); }); it("should insert a input validations arguments", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO input_validation_argument(id, id_input_validation, placement, argument)\ - VALUES\ - (5, 1, 2, '10'),\ - (6, 2, 2, '2');", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO input_validation_argument(id, id_input_validation, placement, argument)\ + VALUES\ + (5, 1, 2, '10'),\ + (6, 2, 2, '2');", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(2); done(); - }); + }); it("should select all input validations arguments", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM input_validation_argument;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM input_validation_argument;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(5); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(6); done(); }); }); it("should remove non existent input validations arguments", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_validation_argument WHERE id=11;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM input_validation_argument WHERE id=11;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); done(); - }); }); it("should remove existent input validations arguments", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_validation_argument WHERE id=5 OR id=6;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM input_validation_argument WHERE id=5 OR id=6;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(2); done(); - }); }); it("should insert a form answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO form_answer(id, id_form, answered_at)\ - VALUES\ - (7, 2, '2018-07-02 10:10:25-03'),\ - (8, 3, '2018-06-03 10:11:25-03');", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO form_answer(id, id_form, answered_at)\ + VALUES\ + (7, 2, '2018-07-02 10:10:25-03'),\ + (8, 3, '2018-06-03 10:11:25-03');", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(2); done(); - }); }); it("should select all form answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM form_answer;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM form_answer;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(7); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(8); done(); }); }); it("should remove non existent form answer", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM form_answer WHERE id=11;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM form_answer WHERE id=11;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); done(); - }); }); it("should remove existent form answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM form_answer WHERE id=7 OR id=8;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + dbhandler.executeQuery("DELETE FROM form_answer WHERE id=7 OR id=8;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(2); done(); - }); + }); it("should insert a input answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("INSERT INTO input_answer(id, id_form_answer, id_input, value, placement)\ - VALUES\ - (15,1, 6,'Answer to Question 1 Form 3',1),\ - (16,1, 7,'Answer to Question 2 Form 3',2);", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - + dbhandler.executeQuery("INSERT INTO input_answer(id, id_form_answer, id_input, value, placement)\ + VALUES\ + (15,1, 6,'Answer to Question 1 Form 3',1),\ + (16,1, 7,'Answer to Question 2 Form 3',2);", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("INSERT"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("INSERT"); + expect(result.rowCount).to.be.equal(2); done(); - }); }); it("should select all form answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("SELECT * FROM input_answer;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results: any[]) => { + dbhandler.executeQuery("SELECT * FROM input_answer;", (err: Error, result?: QueryResult) => { expect(err).to.be.a("null"); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("SELECT"); - expect(results[1].rowCount).to.be.equal(15); - expect(results[2].command).to.be.equal("COMMIT"); + expect(result.command).to.be.equal("SELECT"); + expect(result.rowCount).to.be.equal(16); done(); }); }); it("should remove non existent input answer", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_answer WHERE id=17;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { + dbhandler.executeQuery("DELETE FROM input_answer WHERE id=17;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(0); + done(); + }); + }); - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(0); - expect(results[2].command).to.be.equal("COMMIT"); + it("should remove existent input answers", (done) => { + dbhandler.executeQuery("DELETE FROM input_answer WHERE id=15 OR id=16;", (err: Error, result?: QueryResult) => { + expect(err).to.be.a("null"); + expect(result.command).to.be.equal("DELETE"); + expect(result.rowCount).to.be.equal(2); done(); + }); + }); +}); +describe("Read and Write on Database", () => { + const dbhandler = new DbHandler(configs.poolconfig); + it("should read an existent form", (done) => { + dbhandler.readForm(1, (err: Error, form: Form) => { + expect(err).to.be.a("null"); + expect(form.id).to.be.equal(1); + expect(form.title).to.be.equal("Form Title 1"); + expect(form.description).to.be.equal("Form Description 1"); + expect(form.inputs.length).to.be.equal(3); + expect(form.inputs[0].id).to.be.equal(1); + expect(form.inputs[0].placement).to.be.equal(0); + expect(form.inputs[0].description).to.be.equal("Description Question 1 Form 1"); + expect(form.inputs[0].question).to.be.equal("Question 1 Form 1"); + expect(form.inputs[0].type).to.be.equal(InputType.TEXT); + expect(form.inputs[0].validation.length).to.be.equal(0); + + expect(form.inputs[1].id).to.be.equal(3); + expect(form.inputs[1].placement).to.be.equal(1); + expect(form.inputs[1].description).to.be.equal("Description Question 2 Form 1"); + expect(form.inputs[1].question).to.be.equal("Question 2 Form 1"); + expect(form.inputs[1].type).to.be.equal(InputType.TEXT); + expect(form.inputs[1].validation.length).to.be.equal(2); + expect(form.inputs[1].validation[0].type).to.be.equal(ValidationType.MAXCHAR); + expect(form.inputs[1].validation[0].arguments.length).to.be.equal(1); + expect(form.inputs[1].validation[0].arguments[0]).to.be.equal("10"); + expect(form.inputs[1].validation[1].type).to.be.equal(ValidationType.MINCHAR); + expect(form.inputs[1].validation[1].arguments.length).to.be.equal(1); + expect(form.inputs[1].validation[1].arguments[0]).to.be.equal("2"); + + expect(form.inputs[2].id).to.be.equal(2); + expect(form.inputs[2].placement).to.be.equal(2); + expect(form.inputs[2].description).to.be.equal("Description Question 3 Form 1"); + expect(form.inputs[2].question).to.be.equal("Question 3 Form 1"); + expect(form.inputs[2].type).to.be.equal(InputType.TEXT); + expect(form.inputs[2].validation.length).to.be.equal(2); + expect(form.inputs[2].validation[0].type).to.be.equal(ValidationType.REGEX); + expect(form.inputs[2].validation[0].arguments.length).to.be.equal(1); + expect(form.inputs[2].validation[0].arguments[0]).to.be.equal("\\d{5}-\\d{3}"); + expect(form.inputs[2].validation[1].type).to.be.equal(ValidationType.MANDATORY); + expect(form.inputs[2].validation[1].arguments.length).to.be.equal(0); + + done(); }); }); - it("should remove existent input answers", (done) => { - series([ - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.begin(cb); - }, - (callback: (err: Error, result?: QueryResult) => void) => { - dbhandler.executeQuery("DELETE FROM input_answer WHERE id=15 OR id=16;", callback); - }, - (cb: (err: Error, result?: QueryResult) => void) => { - dbhandler.commit(cb); - } - ], (err, results) => { - expect(results[0].command).to.be.equal("BEGIN"); - expect(results[1].command).to.be.equal("DELETE"); - expect(results[1].rowCount).to.be.equal(2); - expect(results[2].command).to.be.equal("COMMIT"); + it("should read a non existent form", (done) => { + dbhandler.readForm(10, (err: Error, form: Form) => { + expect(err).to.not.equal(null); + expect(form).to.be.undefined; + done(); + }); + }); + + it("should write form", (done) => { + + let validation: Validation[]; + validation = [{type: ValidationType.MANDATORY, arguments: []}]; + let input = new Input (1, "Description 1", "Question 1", InputType.TEXT, validation); + const inputs: Input[] = []; + inputs.push(input); + validation = [{type: ValidationType.MINCHAR, arguments: [5]}]; + + input = new Input (2, "Description 2", "Question 2", InputType.TEXT, validation); + inputs.push(input); + const form = new Form("Form Title 1", "Form Description 1", inputs); + dbhandler.writeForm(form, (err: Error, formResult: Form) => { + expect(err).to.be.a("null"); + expect(formResult.id).to.be.equal(4); + let inputId: number = 8; + for (input of formResult.inputs){ + expect(input.id).to.be.equal(inputId); + inputId++; + } + done(); }); + }); + }); diff --git a/src/utils/dbHandler.ts b/src/utils/dbHandler.ts index 366609b9eacd5d3d8704aea8bbe39bb2aad69e8c..52dc514c1c4edee119ee9b8f734578befeffb642 100644 --- a/src/utils/dbHandler.ts +++ b/src/utils/dbHandler.ts @@ -19,7 +19,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { eachOfSeries, eachSeries , map, waterfall } from "async"; import { Pool, PoolConfig, QueryResult } from "pg"; +import { Form } from "../core/form"; +import { Input, Validation } from "../core/input"; +import { EnumHandler, InputType, ValidationType } from "./enumHandler"; /** * Class of the SGBD from the Form Creator Api perspective. Used to @@ -45,13 +49,14 @@ export class DbHandler { * Asynchronously executes a query and get its result. * @param query - Query (SQL format) to be executed. * @param cb - Callback function which contains the data read. - * @param cb.error - Error information when the method fails. + * @param cb.err - Error information when the method fails. * @param cb.result - Query result. */ public executeQuery(query: string, cb: (err: Error, result?: QueryResult) => void): void{ this.pool.connect((err, client, done) => { + if (err) { cb(err); return; @@ -71,6 +76,12 @@ export class DbHandler { public commit(cb: (err: Error, result?: QueryResult) => void){ this.executeQuery("COMMIT;", cb); } + /** + * Asynchronously rollback a transaction + */ + public rollback(cb: (err: Error, result?: QueryResult) => void){ + this.executeQuery("ROLLBACK;", cb); + } /** * Asynchronously starts a transaction @@ -79,4 +90,340 @@ export class DbHandler { this.executeQuery("BEGIN;", cb); } + /** + * Asynchronously executes a query and get a Form. + * @param id - Form identifier to be founded. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.form - Form or null if form not exists. + */ + public readForm(id: number, cb: (err: Error, form?: Form) => void){ + const query: string = "SELECT id, title, description FROM form WHERE id=" + id + ";"; + + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin(callback); + }, + (result: QueryResult, callback: (err: Error, result?: QueryResult) => void) => { + this.executeQuery(query, callback); + }, + (result: QueryResult, callback: (err: Error, form?: Form) => void) => { + + if (result.rowCount !== 1){ + callback(new Error("Bad number of ids returned: found '" + result.rowCount + "' should be 1")); + return; + } + this.readInputWithFormId(id, (error: Error, inputs: Input[]) => { + const formTmp = new Form ( + result.rows[0]["title"], + result.rows[0]["description"], + inputs, + result.rows[0]["id"] + ); + callback(error, formTmp); + }); + }, + (form: Form, callback: (err: Error, form?: Form) => void) => { + + this.commit((error: Error, result?: QueryResult) => { + callback(error, form); + }); + } + ], (err, result: Form) => { + + if (err){ + this.rollback( (error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(err, result); + + }); + + } + + /** + * A private method to asynchronously executes a query and get a list of Inputs. + * @param id - Form identifier which inputs are linked to. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.inputs - Input array or an empty list if there is no input linked to form. + */ + private readInputWithFormId(id: number, cb: (err: Error, inputs?: Input[]) => void){ + const query: string = "SELECT id, placement, input_type, question, description FROM input WHERE id_form=" + id + ";"; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + map(result.rows, (innerResult, callback) => { + this.readInputValidationWithInputId(innerResult["id"], (error: Error, validation: Validation[]) => { + const inputTmp = new Input ( + innerResult["placement"], + innerResult["description"], + innerResult["question"], + EnumHandler.parseInputType(innerResult["input_type"]), + validation, + innerResult["id"]); + callback(error, inputTmp); + }); + }, (errors, inputs: Input[]) => { + + if (errors){ + this.rollback( (error: Error, results?: QueryResult) => { + cb(errors); + return; + }); + return; + } + + const sortedInputs: Input[] = this.sortByPlacement(inputs); + + cb(errors, sortedInputs); + }); + }); + } + + /** + * A private method to asynchronously executes a query and get a list of Validations based on a Input id. + * @param id - Input identifier which validations are linked to. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.validations - Validation array or an empty list if there is no validation for selected input. + */ + private readInputValidationWithInputId(id: number, cb: (err: Error, validations?: Validation[]) => void){ + const query: string = "SELECT id, validation_type FROM input_validation WHERE id_input=" + id + ";"; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + map(result.rows, (innerResult, callback) => { + this.readInputValidationArgumentWithInputValidationId(innerResult["id"], (error, argumentsArray) => { + const validationTmp: Validation = { + type: EnumHandler.parseValidationType(innerResult["validation_type"]), + arguments: argumentsArray + }; + callback(error, validationTmp); + }); + }, (errors, validation: Validation[]) => { + cb(errors, validation); + }); + }); + + } + + /** + * A private method to asynchronously executes a query and get a list of Validation Argument based on a Validation id. + * @param id - Validation identifier which Validation Arguments are linked to. + * @param cb - Callback function which contains the data read. + * @param cb.err - Error information when the method fails. + * @param cb.argumentsArray - Validation Arguments array or an empty list if there is no validation argument for selected input. + */ + private readInputValidationArgumentWithInputValidationId(id: number, cb: (err: Error, argumentsArray?: any[]) => void){ + const query: string = "SELECT id, argument, placement FROM \ + input_validation_argument WHERE \ + id_input_validation=" + id + ";"; + + // cb(null); + + this.executeQuery(query, (error: Error, result?: QueryResult) => { + + const sortedResult: any[] = this.sortByPlacement(result.rows); + const argumentArrayTmp = []; + for (const innerResult of sortedResult){ + argumentArrayTmp.push(innerResult["argument"]); + } + + cb(error, argumentArrayTmp); + }); + + } + + /** + * A private method to return a array sorted by placement field + * I personally don't think that this method should be here, it is quite weird to have a sort method on database handler + * @param array - Array with objects that have placement field + * @returns - A sorted array by placement + */ + private sortByPlacement(array: any[]): any[] { + + const sortedArray: any[] = array.sort((obj1, obj2) => { + if (obj1["placement"] > obj2["placement"]) { + return 1; + } + + if (obj1["placement"] < obj2["placement"]) { + return -1; + } + + return 0; + }); + + return sortedArray; + } + + /** + * Asynchronously insert a form on Database and return it. + * @param form - Form to be inserted. + * @param cb - Callback function which contains the inserted data. + * @param cb.err - Error information when the method fails. + * @param cb.formResult - Form or null if form any error occurs. + */ + + public writeForm(form: Form, cb: (err: Error, formResult?: Form) => void){ + const query: string = "INSERT INTO form (title, description) VALUES\ + ( \'" + form.title + "\', \'" + + form.description + "\' ) RETURNING id;"; + + waterfall([ + (callback: (err: Error, result?: QueryResult) => void) => { + this.begin(callback); + }, + (result: QueryResult, callback: (err: Error, result?: QueryResult) => void) => { + this.executeQuery(query, callback); + }, + (result: QueryResult, callback: (err: Error, formId?: number) => void) => { + if (result.rowCount !== 1){ + callback(new Error("Form not inserted")); + return; + } + eachSeries(form.inputs, (input: Input, innerCallback) => { + this.writeInputWithFormId(result.rows[0]["id"], input, innerCallback); + }, (error) => { + if (error){ + callback(error); + return; + + } + callback(error, result.rows[0]["id"]); + + }); + }, + (formId: number, callback: (err: Error, formId?: number) => void) => { + + this.commit((error: Error, result?: QueryResult) => { + callback(error, formId); + }); + }, + (formId: number, callback: (err: Error, formResult?: Form) => void) => { + + this.readForm(formId, callback); + + } + ], (err, result: Form) => { + + if (err){ + this.rollback( (error: Error, results?: QueryResult) => { + cb(err); + return; + }); + return; + } + cb(err, result); + + }); + } + + /** + * Asynchronously insert a form on Database and return it. + * @param formId - Form identifier to relate with Input. + * @param input - Input to be inserted. + * @param cb - Callback function. + * @param cb.err - Error information when the method fails. + */ + private writeInputWithFormId(formId: number, input: Input, cb: (err: Error) => void){ + const query: string = "INSERT INTO input (\ + id_form, placement, input_type, question, description)\ + VALUES ( " + formId + ", " + input.placement + + ", \'" + EnumHandler.stringfyInputType(input.type) + + "\', \'" + input.question + "\', \'" + input.description + + "\' ) RETURNING id;"; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + if (err){ + cb(err); + return; + } + + if (result.rowCount !== 1){ + cb(new Error("Input not Inserted")); + return; + } + + eachSeries(input.validation, (validation: Validation, callback) => { + + this.writeValidationWithInputId(result.rows[0]["id"], validation, callback); + + }, (error) => { + + cb(error); + + }); + }); + + } + /** + * Asynchronously insert a form on Database and return it. + * @param inputId - Input id to relate with Validation. + * @param validation - Validation to be inserted. + * @param cb - Callback function. + * @param cb.err - Error information when the method fails. + */ + private writeValidationWithInputId(inputId: number, validation: Validation, cb: (err: Error) => void){ + const query: string = "INSERT INTO input_validation ( id_input, \ + validation_type) VALUES ( " + inputId + ", \'" + + EnumHandler.stringfyValidationType(validation.type) + + "\' ) RETURNING id;"; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + + if (err){ + cb(err); + return; + } + + if (result.rowCount !== 1){ + cb(new Error("Validation not Inserted")); + return; + } + + eachOfSeries(validation.arguments, (argument, placement: number, callback) => { + + this.writeValidationArgumentWithInputIdAndPlacement(result.rows[0]["id"], argument, placement, callback); + }, (error) => { + cb(error); + }); + + }); + + } + + /** + * Asynchronously insert a form on Database and return it. + * @param validationId - Validation identifier to relate with Argument. + * @param argument - Argument to be inserted. + * @param placement - placement where argument should be. + * @param cb - Callback function. + * @param cb.err - Error information when the method fails. + */ + private writeValidationArgumentWithInputIdAndPlacement(validationId: number, argument: any, placement: number, cb: (err: Error) => void){ + const query: string = "INSERT INTO input_validation_argument \ + ( id_input_validation, argument, placement )\ + VALUES ( \'" + validationId + "\', \'" + + argument + "\', \'" + placement + "\' ) RETURNING id;"; + + this.executeQuery(query, (err: Error, result?: QueryResult) => { + + if (err){ + cb(err); + return; + } + + if (result.rowCount !== 1){ + cb(new Error("Validation Argument not Inserted")); + return; + } + + cb(err); + + }); + + } } diff --git a/src/utils/enumHandler.ts b/src/utils/enumHandler.ts index 22cf1fcb4ca11d44d0a8bd3c2dfaeb9c975c8432..247325fa8e573f150977ad1fe697a94a1631e4e1 100644 --- a/src/utils/enumHandler.ts +++ b/src/utils/enumHandler.ts @@ -68,7 +68,7 @@ export class EnumHandler { * @returns - Matching InputType */ public static parseInputType(str: string): InputType { - str = str.toLocaleLowerCase(); + str = str.toLocaleLowerCase().trimLeft().trimRight(); switch (str) { case "text": return InputType.TEXT; @@ -102,7 +102,7 @@ export class EnumHandler { * @returns - Matching ValidationType */ public static parseValidationType(str: string): ValidationType { - str = str.toLocaleLowerCase(); + str = str.toLocaleLowerCase().trimLeft().trimRight(); switch (str) { case "mandatory": return ValidationType.MANDATORY; diff --git a/src/utils/validationHandler.spec.ts b/src/utils/validationHandler.spec.ts index 3d3f3bf9a353397eecf7fb5a7a16cc72623c53ff..2e0da2d91f86fbcaf3f141366ab578913224faaf 100644 --- a/src/utils/validationHandler.spec.ts +++ b/src/utils/validationHandler.spec.ts @@ -27,7 +27,7 @@ import {ValidationHandler } from "./validationHandler"; describe("Validation Handler", () => { it("should test when Input is mandatory", () => { const validation: Validation[] = [{type: ValidationType.MANDATORY, arguments: []}]; - const input = new Input (1, "Question", InputType.TEXT, validation); + const input = new Input (1, "Description", "Question", InputType.TEXT, validation); expect(ValidationHandler.validateInput(input, "FOOL")).to.be.true; expect(ValidationHandler.validateInput(input, "")).to.be.false; @@ -37,7 +37,7 @@ describe("Validation Handler", () => { it("should test when Input has a regex", () => { const validation: Validation[] = [{type: ValidationType.REGEX, arguments: ["\\d{5}-\\d{3}"]}]; - const input = new Input (1, "Question", InputType.TEXT, validation); + const input = new Input (1, "Description", "Question", InputType.TEXT, validation); expect(ValidationHandler.validateInput(input, "88888-888")).to.be.true; expect(ValidationHandler.validateInput(input, "88888-88")).to.be.false; @@ -48,7 +48,7 @@ describe("Validation Handler", () => { it("should test when Input has a minimum char number", () => { const validation: Validation[] = [{type: ValidationType.MINCHAR, arguments: [5]}]; - const input = new Input (1, "Question", InputType.TEXT, validation); + const input = new Input (1, "Description", "Question", InputType.TEXT, validation); expect(ValidationHandler.validateInput(input, "12345")).to.be.true; expect(ValidationHandler.validateInput(input, "123456")).to.be.true; @@ -59,7 +59,7 @@ describe("Validation Handler", () => { }); it("should test when Input has a maximum char number", () => { const validation: Validation[] = [{type: ValidationType.MAXCHAR, arguments: [5]}]; - const input = new Input (1, "Question", InputType.TEXT, validation); + const input = new Input (1, "Description", "Question", InputType.TEXT, validation); expect(ValidationHandler.validateInput(input, "1234")).to.be.true; expect(ValidationHandler.validateInput(input, "12345")).to.be.true;