Commit 43616005 authored by Stephanie Briere Americo's avatar Stephanie Briere Americo
Browse files

Merge branch 'develop' into 'master'

Stable version 1.2

See merge request !65
parents d5a794ea 4ccc1cee
Pipeline #22789 passed with stages
in 2 minutes and 24 seconds
......@@ -7,6 +7,7 @@
*.swo
/.trash
/pids
/vscode
/logs
/node_modules
/doc
......@@ -18,4 +19,6 @@
docker-compose.yaml
.editorconfig
package-lock.json
*~
\ No newline at end of file
*~
.#*
*#
......@@ -10,8 +10,14 @@ variables:
POSTGRES_USER: 'runner'
POSTGRES_PASSWORD: '123mudar'
POSTGRES_PORT: 5432
DB_USER: 'runner'
DB_NAME: 'form-creator'
DB_HOST: 'postgres'
DB_PASSWORD: '123mudar'
DB_PORT: 5432
image_version: ''
DOCKER_URL: 'dockerregistry.c3sl.ufpr.br:5000/c3sl/simmctic-form-creator-api'
GIT_SUBMODULE_STRATEGY: recursive
cache:
paths:
......@@ -31,12 +37,6 @@ run_test:
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
- apt-get update -q -y
- apt-get install -y postgresql-client-10
- git clone --recurse-submodules https://gitlab.c3sl.ufpr.br/simmctic/form-creator/form-creator-database.git form-creator-database
- cd form-creator-database
- psql-manager/manager.sh create workspace
- psql-manager/manager.sh fixture workspace
- cd ..
- rm -rf form-creator-database
- yarn install --frozen-lockfile --silent --non-interactive
- ln -s config.env.example config/test.env
- yarn test
......
[submodule "form-creator-database"]
path = form-creator-database
url = https://gitlab.c3sl.ufpr.br/simmctic/form-creator/form-creator-database.git
\ No newline at end of file
......@@ -4,10 +4,128 @@ All notable changes to this project will be documented in this file.
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).
## 1.2.0 - 19-02-2020
### Changed
- Created a stable version with user
## 1.1.13 - 10-02-2020
### Added
- Route to read Form Answer #66 (Richard Heise & Gianfranco)
- Method to get IDs from form answers from a form
- Read all answers from a form
- Scenario form test to read
## 1.1.12 - 04-02-2020
### Added
- Route to update an user #65 (Richard Heise)
## Changed
- Opthandler can create user without hash
- UserQueryBuilder Update now needs an id
- UserOptions hash is not obrigatory
## 1.1.11 - 04-02-2020
## Changed
- Form controller update route to verify if a user own the form #62 (Gianfranco)
## 1.1.10 - 03-02-2020
### Added
- Route to list forms #61 (Richard Heise)
## Changed
- List from FormQueryBuilder now lists an user's forms
## 1.1.9 - 03-02-2020
### Added
- Route to change an user's password #63 (Richard Heise)
## Changed
- Delete route now has the token validation
## 1.1.8 - 30-01-2020
### Added
- Route to assign users to forms #60 (Richard Heise)
## Changed
- Route to write a form now has an extra stage in the waterfall
- This stage assigns the user to a form by ID
## 1.1.7 - 29-01-2020
### Added
- Function to assign users to forms #54 (Gianfranco)
- Assign added to userQueryBuilder file
## 1.1.6 - 24-01-2020
### Added
- Middleware to validate tokens #56 (Richard Heise)
- Route do delete an user #59 (Richard Heise)
- Delete control methods on UserQueryBuilder
## Changed
- Initial user tests are now on the form.spec.ts
## 1.1.5 - 22-01-2020
### Added
- SubForm class #57 (Gianfranco)
- InputType SubForm
- FormQueryBuilder methods to handle with SubForms
- AnswerQueryBuilder methods to handle with SubForms answers
- OptHandler method to validate SubForm
- TestHandler method to compare two SubForms
- Database table for SubForms
## Changed
- Input class to contain a SubForm object
- OptHandler methods to handle SubForms Answers
- Scenario file with new objects to test SubForms
- TestHandler method to compare two SubFormsAnswer
## 1.1.4 - 19-12-2019
### Added
- SignIn route to possibilitate logins #55 (Richard Heise)
- JWT (Json Web Token) library and its types to dependencies
- Tokens can be created once an user is considered valid
## Changed
- UserQueryBuilder has, now, a public function to verify if an email is in the DB
- The requires are now imports for padronization reasons
## 1.1.3 - 12-12-2019
### Added
- UserCtrl class created to control the user routes #52 (Richard Heise)
- The bcrypt library and its types to dependencies
- User route tests
### Changed
- User Query Builder now has writeController to validate an unique email user in the database
## 1.1.2 - 25-11-2019
### Added
- User class to create an user object #53 (Richard Heise)
- User Query Builder to write, read and update an user in the database
- User tests
- An user table in the database
## 1.1.1 - 17-10-2019
### Added
- Fixture class to manage the database #49 (Gianfranco)
- Database module inside the api
- Before test to call fixture class
### Changed
- Database to work with usql-manager
- Config class to receive the new parameters
## 1.1.0 - 11-10-2019
### Changed
- Create a stable version
## 1.0.10 - 10-10-2019
### Added
- Validation type DEPENDENCY #41 (Gianfranco)
......@@ -79,6 +197,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Create a stable version
## 0.0.27 - 15-08-2019
### Added
- DbHandler methods to update database #32 (Gianfranco)
......
......@@ -7,6 +7,14 @@ RUN mkdir -p $WORKSPACE
# Change WORKSPACE owner
RUN chown -R node:node $WORKSPACE
# Install psql
RUN apt-get update -q -y
RUN apt-get install wget gnupg -y
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main" > /etc/apt/sources.list.d/pgdg.list
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN apt-get update -q -y
RUN apt-get install -y postgresql-client-10
# Change running user
USER node
# This sets the context of where commands will be ran in and is documented
......@@ -21,8 +29,6 @@ COPY --chown=node:node package.json .
COPY --chown=node:node yarn.lock .
RUN yarn install --frozen-lockfile --silent --non-interactive
# If you are building your code for production
# RUN yarn install --production --frozen-lockfile --silent --non-interactive
......@@ -34,5 +40,4 @@ COPY --chown=node:node . .
EXPOSE 3000
CMD [ "yarn", "start" ]
......@@ -7,3 +7,6 @@
# You should not modify this file. You should copy it and edit and use the copy.
PORT=3000
# This should be a secret key to encode the informations on the token
JWT_KEY="secret_very_long_string_key"
Subproject commit 6cd530d9f009f739a7f285efddae4d29377dad7c
{
"name": "form-creator-api",
"version": "1.1.0",
"version": "1.2.0",
"description": "RESTful API used to manage and answer forms.",
"main": "index.js",
"scripts": {
......@@ -18,6 +18,7 @@
"license": "ISC",
"dependencies": {
"@types/async": "^2.0.50",
"@types/bcrypt": "^3.0.0",
"@types/express": "^4.16.0",
"@types/pg": "^7.4.13",
"async": "^2.6.1",
......@@ -28,10 +29,13 @@
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/jsonwebtoken": "^8.3.5",
"@types/mocha": "^5.2.5",
"@types/supertest": "^2.0.7",
"bcrypt": "^3.0.7",
"chai": "^4.2.0",
"istanbul": "1.1.0-alpha.1",
"jsonwebtoken": "^8.5.1",
"mocha": "^5.2.0",
"supertest": "^3.3.0",
"tslint": "^5.13.1",
......
......@@ -31,6 +31,7 @@ import { DbHandler } from "../utils/dbHandler";
* return a error the extension must be made.
*/
export interface Request extends express.Request {
userData: string | object;
/** A Database handler instace */
db: DbHandler;
}
......
This diff is collapsed.
......@@ -29,27 +29,6 @@ import { Request } from "../apiTypes";
export class FormCtrl {
public static list(req: Request, res: Response, next: NextFunction) {
req.db.form.list((err: Error, forms?: Form[]) => {
if (err){
res.status(500).json({
message: "Could not list forms. Some error has occurred. Check error property for details.",
error: err
});
return;
}
const mappedForms = forms.map(form => ({
id: form.id
, title: form.title
, description: form.description
}));
res.json(mappedForms);
return;
});
}
public static read(req: Request, res: Response, next: NextFunction) {
req.db.form.read(req.params.id, (err: Error, form?: Form) => {
......@@ -71,7 +50,7 @@ export class FormCtrl {
let form: Form;
try {
form = new Form(OptHandler.form(req.body));
} catch(e) {
} catch (e) {
res.status(500).json({
message: "Invalid Form. Check error property for details.",
error: e.message
......@@ -92,16 +71,34 @@ export class FormCtrl {
, inputs: []
};
const formUpdate: FormUpdate = DiffHandler.diff(formResult, new Form (formOpts));
callback(null, formUpdate);
});
},
(formUpdate: FormUpdate, callback: (err: Error, formId: number) => void) => {
(formUpdate: FormUpdate, callback: (err: Error, formId?: number) => void) => {
req.db.form.update(formUpdate, (err: Error) => {
callback(err, formUpdate.form.id);
if (err) {
callback(err);
return;
}
callback(null, formUpdate.form.id);
});
},
(formId: number, callback: (err: Error) => void) => {
/** req.userData has to be parsed into an object so we can get its ID */
req.db.user.assign(Object(req.userData).id, formId, (err: Error) => {
if (err) {
callback(err);
return;
}
callback(null);
});
}
], (err, resultId) => {
], (err) => {
if (err) {
res.status(500).json({
message: "Could not insert form. Some error has occurred. Check error property for details."
......@@ -109,10 +106,9 @@ export class FormCtrl {
});
return;
}
res.json({
id: resultId
, message: "Form added. Id on key 'id'"
message: "Form added."
});
return;
});
......@@ -132,19 +128,30 @@ export class FormCtrl {
return;
}
waterfall([
(callback: (err: Error) => void) => {
req.db.form.list(Object(req.userData).id, (err: Error, forms?: Form[]) => {
if (err) {
callback(err);
return;
}
const e: Error = new Error("User dont own this form.");
callback((forms.some((obj) => obj.id === Number(req.params.id))) ? null : e);
});
},
(callback: (err: Error, result?: FormUpdate) => void) => {
req.db.form.read(req.params.id, (err: Error, oldForm: Form) => {
if (err) {
callback(err);
return;
}
const formUpdate: FormUpdate = DiffHandler.diff(newForm, oldForm);
callback(null, formUpdate);
});
},
(formUpdate: FormUpdate, callback: (err: Error, formUpdateResult?: FormUpdate) => void) => {
(formUpdate: FormUpdate, callback: (err: Error, formResult?: Form) => void) => {
req.db.form.update(formUpdate, callback);
}
], (err) => {
......
......@@ -21,12 +21,16 @@
import * as request from "supertest";
import { expect } from "chai";
import { formAnswerScenario, dbHandlerScenario } from "../../../test/scenario";
import * as server from "../../main";
import { EnumHandler,InputType, ValidationType } from "../../utils/enumHandler";
import { TestHandler } from "../../utils/testHandler";
import { OptHandler } from "../../utils/optHandler";
import { Form, FormOptions } from "../../core/form";
import { Input, InputOptions, Validation } from "../../core/input";
import { testToken } from "./form.spec";
import { FormAnswer, FormAnswerOptions } from "../../core/formAnswer";
const util = require('util');
describe("API data controller", () => {
......@@ -35,11 +39,7 @@ describe("API data controller", () => {
request(server)
.post("/answer/1")
.send({
1:["Answer to Question 1 Form 1"]
, 2:["12345-000"]
, 3:["MAXCHAR 10"]
})
.send(formAnswerScenario.validAnswer)
.expect(200)
.expect((res: any) => {
expect(res.body.id).to.be.equal(7);
......@@ -52,11 +52,7 @@ describe("API data controller", () => {
request(server)
.post("/answer/1")
.send({
1:["Answer to Question 1 Form 1"]
, 2:["12a345-000"]
, 3:["MAXCHAR 10 AND MORE"]
})
.send(formAnswerScenario.invalidAnswer)
.expect(500)
.expect((res: any) => {
const message = "Could not Create form Answer. Some error has occurred. Check error property for details.";
......@@ -73,11 +69,7 @@ describe("API data controller", () => {
request(server)
.post("/answer/10")
.send({
1:["Answer to Question 1 Form 1"]
, 2:["12a345-000"]
, 3:["MAXCHAR 10 AND MORE"]
})
.send(formAnswerScenario.validAnswer)
.expect(500)
.expect((res: any) => {
const message = "Form with id: '10' not found. Some error has occurred. Check error property for details.";
......@@ -87,4 +79,32 @@ describe("API data controller", () => {
})
.end(done);
});
it("Should respond 200 when reading a form answer", (done) => {
request(server)
.get("/answer/1")
.set("Authorization", "bearer " + testToken)
.expect(200)
.expect((res: any) => {
for (let i = 0; i < 3; ++i) {
TestHandler.testFormAnswer(res.body[i], formAnswerScenario.formAnswerRead[i]);
}
})
.end(done);
});
it("Should respond 500 when failing to read a form answer", (done) => {
request(server)
.get("/answer/500")
.set("Authorization", "bearer " + testToken)
.expect(500)
.expect((res: any) => {
expect(res.body.error).to.be.equal("User dont own this form.");
})
.end(done);
});
});
......@@ -26,6 +26,8 @@ import { FormAnswer, FormAnswerOptions } from "../../core/formAnswer";
import { ValidationHandler } from "../../utils/validationHandler";
import { Response, NextFunction } from "express";
import { Request } from "../apiTypes";
import { waterfall } from "async";
const util = require('util');
export class AnswerCtrl {
......@@ -39,7 +41,7 @@ export class AnswerCtrl {
});
return;
}
let inputAnswerOptionsDict: InputAnswerOptionsDict = {}
for (const key of Object.keys(req.body)) {
......@@ -67,7 +69,7 @@ export class AnswerCtrl {
if (err){
throw err;
return;
}
}
res.json({
id: formAnswerResult.id
, message: "Answered"
......@@ -81,7 +83,7 @@ export class AnswerCtrl {
, error: e.validationDict
});
return;
}
}
res.status(500).json({
message: "Could not Create form Answer. Some error has occurred. Check error property for details."
......@@ -91,4 +93,39 @@ export class AnswerCtrl {
}
});
}
public static read(req: Request, res: Response, next: NextFunction) {
waterfall([
(callback: (err: Error) => void) => {
req.db.form.list(Object(req.userData).id, (err: Error, forms?: Form[]) => {
if (err) {
callback(err);
return;
}
const e: Error = new Error("User dont own this form.");
callback((forms.some((obj) => obj.id === Number(req.params.id))) ? null : e);
});
},
(callback: (err: Error, answer?: FormAnswer[]) => void) => {
req.db.answer.readAll(req.params.id, (err: Error, resultAnswer?: FormAnswer[]) => {
if (err) {
callback(err);
return;
}
res.status(200).json( resultAnswer )
});
}
], (error: Error) => {
if (error) {
res.status(500).json({
message: "Some error has ocurred. Check error property for details.",
error: error.message
});
return;
}
});
}
}
/*
* form-creator-api. RESTful API to manage and answer forms.
* Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre
* Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
*
* This file is part of form-creator-api.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import * as request from "supertest";
import { expect } from "chai";
import * as server from "../../main";
import { testToken } from "./form.spec";
describe ("API data controller", () => {
it ("Should respond 500 when failing to sign up a hash-null user", (done) => {
request(server)
.post("/user/signUp")
.send({
name: "Test_name"
, email: "test_email@test.com"
, hash: null
})
.expect(500)
.expect((res: any) => {
expect(res.body).to.be.an("object");
expect(res.body.message).to.be.an("string");
expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details.");
expect(res.body.error).to.be.equal("data and salt arguments required");
})
.end(done);
});
it ("Should respond 500 when failing sign up a name-null user", (done) => {
request(server)
.post("/user/signUp")
.send({
email: "test_email@test.com"
, hash: "Test_pw"
})
.expect(500)
.expect((res: any) => {
expect(res.body).to.be.an("object");
expect(res.body.message).to.be.an("string");
expect(res.body.message).to.be.equal("Some error has ocurred. Check error property for details.");
expect(res.body.error).to.be.equal("The dataType named 'name' was not found")
})
.end(done);
});
it ("Should respond 500 when failing sign up an email-null user", (done) => {
request(server)