Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
simmctic
form-creator
form-creator-api
Commits
9673fa3b
Commit
9673fa3b
authored
Apr 09, 2019
by
Matheus Horstmann
🐴
Committed by
Lucas Fernandes de Oliveira
Apr 09, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue
#7
: Write and read Inputs and Forms
Signed-off-by:
Matheus Horstmann
<
mch15@inf.ufpr.br
>
parent
25427545
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
604 additions
and
419 deletions
+604
-419
CHANGELOG.md
CHANGELOG.md
+10
-0
package.json
package.json
+1
-1
src/core/form.ts
src/core/form.ts
+14
-10
src/core/input.ts
src/core/input.ts
+10
-1
src/utils/dbHandler.spec.ts
src/utils/dbHandler.spec.ts
+215
-400
src/utils/dbHandler.ts
src/utils/dbHandler.ts
+348
-1
src/utils/enumHandler.ts
src/utils/enumHandler.ts
+2
-2
src/utils/validationHandler.spec.ts
src/utils/validationHandler.spec.ts
+4
-4
No files found.
CHANGELOG.md
View file @
9673fa3b
...
...
@@ -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)
...
...
package.json
View file @
9673fa3b
{
"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"
:
{
...
...
src/core/form.ts
View file @
9673fa3b
...
...
@@ -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
;
}
}
src/core/input.ts
View file @
9673fa3b
...
...
@@ -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
;
...
...
src/utils/dbHandler.spec.ts
View file @
9673fa3b
This diff is collapsed.
Click to expand it.
src/utils/dbHandler.ts
View file @
9673fa3b
...
...
@@ -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.err
or
- 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
);
});
}
}
src/utils/enumHandler.ts
View file @
9673fa3b
...
...
@@ -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
;
...
...
src/utils/validationHandler.spec.ts
View file @
9673fa3b
...
...
@@ -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
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment