-
Install all dependencies:
- Node.js v8 or greater
- Postgres (v9.6 or greater)
Note: Default installations of Postgres on macOS (such as through homebrew or DMG install) may not grant proper permission to your postgres user role. macOS users may need to alter their Postgres user role with role attribute
LOGIN
. See ALTER ROLE – (postgres.org) in the Postgres Documentation for more.Note: Windows users may be required to install Python 2.7 and Visual C++ Build Tools. Please follow Installing Python and Visual C++ Build Tools (Windows) prior to continuing installation.
-
Install dependencies:
yarn
-
Create a
.env
file in root:cp .env.example .env
Note: See Configuration for more about configuring your
.env
file.
-
Create database:
yarn create_db
-
Populate database:
yarn seed
-
Run:
yarn serve
Due to variances between Windows, Linux, and macOS, Windows users will have to add a few steps for installing the needed components for node-gyp. And all users will probably have to install Python 2.7 as well.
- Download & install Python 2.7.
- Set the Environmental Variables for the Python install, including the variable 'PYTHON.'
- Download & install Visual C++ Build Tools.
- Run 'npm config set msvs_version 2015 --global'
- If errors continue to occur, update to the latest version of yarn with 'npm install npm -g'
Use export NODE_ENV=development
(or production
or test
) to set node environment in Bash compatible shells or equivalent in others.
Add to PATH
export PATH=$PATH:/Applications/Postgres.app/Contents/Versions/latest/bin
. Note: You'll have to preform this operation for each new shell session, or add the Postgres bin
file to your $PATH
variable.
.env.example
is a a minimal example. To use, cp .env.example .env
.
A list of full environment variable settings is below. They can be either manually set in the shell or can be included in the .env
file. Defaults indicated in paranthesis.
- JWT_SECRET: Secret for JWT encryption ('this is a secret' for development and test).
- AUTH_MICROSERVICE_URL: Base client url for password reset (no default).
- SURVEY_SERVICE_PORT: Port for the API server (9005).
- SURVEY_SERVICE_PG_DB: Database name (surveyService for development and production, surveyServicetest for test).
- SURVEY_SERVICE_PG_USER: Database user (no default).
- SURVEY_SERVICE_PG_PASSWORD: Database password (no default).
- SURVEY_SERVICE_PG_HOST: Database host ip (localhost).
- SURVEY_SERVICE_PG_PORT: Database host port (5432).
- SURVEY_SERVICE_PG_SCHEMA: Database schema in postgres sense. This can be either a single schema name or '~' delimited string of multi tenant schema names.
- SURVEY_SERVICE_DB_DIALECT: Database dialect (postgres only, see here).
- SURVEY_SERVICE_PG_POOL_MAX: Maximum number of connections in pool.
- SURVEY_SERVICE_PG_POOL_MIN: Minimum number of connections in pool.
- SURVEY_SERVICE_PG_POOL_IDLE: The maximum time, in milliseconds, that a connection can be idle before being released.
- SURVEY_SERVICE_PG_SSL: Use secure connections with SSL.
- SURVEY_SERVICE_SUPER_USER_USERNAME: Super user username (super).
- SURVEY_SERVICE_SUPER_USER_PASSWORD: Super user password (Am!d@2017PW).
- SURVEY_SERVICE_SUPER_USER_EMAIL: Super user email (survey_demo@amida.com).
- SURVEY_SERVICE_LOGGING_LEVEL: Logging level (info).
- SURVEY_SERVICE_CRYPT_HASHROUNDS: Number of rounds for hashing user passwords (10).
- SURVEY_SERVICE_CRYPT_RESET_TOKEN_LENGTH: Length for reset password token (20).
- SURVEY_SERVICE_CRYPT_RESET_PASSWORD_LENGTH: Length for temporary random password during reset (10).
- SURVEY_SERVICE_CRYPT_RESET_EXPIRES: Reset password expires value in seconds (3600).
- SURVEY_SERVICE_CORS_ORIGIN: Client URIs that the API CORS setup will accept. Delimited by spaces for multiple URIs e.g. "http://localhost:4000 https://www.example.com"
- SURVEY_SERVICE_ZIP_BASE_URL: Base API URL for Zipwise zip code API. Set to
https://www.zipwise.com/webservices/radius.php
. - SURVEY_SERVICE_ZIP_API_KEY: API key for Zipwise.
yarn start
OR yarn serve
Run server (default port is 9005)
yarn test
Runs all the tests.
yarn test:coverage
Runs all the tests and displays coverage metrics.
yarn lint
Multitenancy is supported through postgres schemas. Multiple schemas are specified using SURVEY_SERVICE_DB_SCHEMA
as a '~' delimited string of schema names. This project assumes that each schema has the same table structure during database synchronization. Schema names are appended to the base url for each API end point so that each tenant can be accessed using a different path.
# deletes db, creates db, runs migrations and then tests
yarn jenkins
# Only run the tests (assumes migrations have been run)
yarn test
# Run test along with code coverage
yarn test:coverage
# Run tests enforcing code coverage (configured via .istanbul.yml)
yarn test:check-coverage
This project primarily uses Mocha, Chai and Super Test for automated testing. Sinon is also used in a couple of tests when it is absolutely necessary to use stubs. Stubbing in general however is avoided.
All tests are located in test
directory in a mostly flat directory structure. All API entries both get a HTTP integration test and an equivalent model test. Unit tests for other utility modules are also included in the root directory. In addition test/use-cases
directory includes informative tests designed to instruct how to use the API from a client.
Individual test suites can be run using mocha. In order to run the tests, make sure you first run createdb surveyServicetest
.
$ mocha test/survey.model.spec.js --bail
Each test in a file may depend on some of the previous tests so using flag bail
is recommended.
Most API resources are documented in snippets in the integration document. A top level script that exercises snippets is also included.
File swagger.json describes the API. There are various swagger tools such as swagger-codegen that can be used view or generate reports based on this file.
When the survey-service api server is running /docs
resource serves Swagger-UI as the API user interface (localhost:9005/docs
for default settings). However due to current limited support for JWT, Swagger-UI mostly works as documentation and resources that require authorization can not be run.
Detailed description of the API with working examples is provided in the integration document.
All table and column names are in snake case to follow Postgres convention and for ability to write Postgres queries easily. All tables have created_at
columns. All tables for which records can be updated have an updated_at
column. All tables for which records can be soft deleted have a deleted_at
column. If there is a timestamp value at the deleted_at
column, the record is soft deleted. No record on any table is ever hard deleted. If exists column line
is used to order records for client presentation.
This is a English first design where all logical records are assumed to be in English when first created. Once a record is created any user facing text column (those users see in the user interface) can be translated to any language. For each table English and translated versions of user facing text colums are stored in an axuilliary table whose name is the name of the actual table postfixed with _text
(Ex: question
and question_text
).
-
answer
: This table stores all the answers to questions. Each record represents an answer to a question (columnquestion_id
) in a survey (columnsurvey_id
) by a user (columnuser_id
). Actual answer data can be a choice from question multiple choices (columnquestion_choice_id
), a free value field (columnvalue
) or a file (columnfile_id
). -
answer_identifier
: This table stores client specific (columntype
) identifiers (columidentifier
) for possible answers to questions (columnsquestion_id
,question_choice_id
,multiple_index
). -
answer_rule
: This table stores conditions (columnlogic
) for survey (columnsurvey_id
) questions (columnquestion_id
) or sections (columnsection_id
) to be enabled or disabled. Conditions are based on answers to other questions (columnanswer_question_id
). Conditon answers themselves are defined in tableanswer_rule_value
. -
answer_rule_logic
: This table defines possible condition types (columnname
, exs: equals, exists) that can be used inanswer_rule
table. -
answer_rule_value
: This table stores answers (columnsquestion_choice_id
andvalue
) that are used in rules (columnanswer_rule_id
) in conditional questions. -
answer_type
: This table stores available answer types. Current supported types aretext
,bool
andchoice
. -
assessment
: This table defines assesments (columnname
) together with tableassesment_survey
. Assessment are set of surveys whose answers are tracked over time. -
assessment_survey
: This table stores the surveys (columnsurvey_id
) that forms an assessment (columnassessment_id
). -
choice_set
: This table defines a choice set (columnreference
) that can be used to for shared choices that can be used in questions. -
file
: This table stores users' (columnuser_id
) answers that are files (columnsname
andcontent
). -
filter
: This table stores quesion filters. -
filter_answer
: This table stores filter specifics (columnsquestion_id
,exclude
,question_choice_id
,value
) for filter (columnfilter_id
). -
language
: Each record in this table represents a supported language.code
column is used as the primary key and designed to store two or three character ISO codes. Columnsname
andnative_name
can be used for language selection on the client. -
question
: Each record in this table represents a question that is being or can be used in surveys . Questions can be stand alone, can belong to a survey or can belong to multiple surveys. Link to surveys (tablesurvey
) is achieved throughsurvey_question
table. Question records can be soft deleted but when no other active record in any other table does not reference it. Versioning is supported using columnsversion
andgroup_id
. Version is a number andgroup_id
is theid
of the first question in the group. A set of types are supported (columntype
). -
question_choice
: Each record in this table represents a choice in multiple choice question of types choice, choiceref, choices or open choice. Each record can belong to a specific question (columnquestion_id
) or to a choice set that can be shared by multiple questions (columnchoice_set_id
). To support composite questions that can have multiply selectable choices together with free text fields (ex: a list of check boxes with a free text other field), this table also stores type of choice (columntype
) with currently supported types ofbool
andtext
. Actual text of choice is stored inquestion_choice_text
. -
question_choice_text
: This table stores translatable columntext
which stores question choice texts.language
is also column and each record has a value fortext
in that language.question_choice_id
column links each record toquestion_choice
table. -
question_identifier
: This table stores client specific (columntype
) identifiers (columidentifier
) for questions (columnsquestion_id
). -
question_text
: This table stores translatable logical question fieldtext
in the column with the same name.language
is also a column and each record has a value fortext
in that language.question_id
column links each record toquestion
table. -
question_type
: This table stores available question types. -
section
: This stores sections that can be used in surveys to group questions. -
section_text
: This table stores translatable logical section fieldstext
anddescription
in the column with the same name.language
is also a column and each record has a value fortext
in that language.ection_id
column links each record toquestion
table. -
survey
: Each record in this table represents a survey. Surveys can be deleted. Versioning is supported using columnsversion
andgroup_id
. Version is a number andgroup_id
is theid
of the first survey in the group. Questions in surveys are represented using another tablesurvey_question
. Only actual data column ismeta
which is designed to store client settings. -
survey_identifier
: This table stores client specific (columntype
) identifiers (columidentifier
) for surveys (columnssurvey_id
). -
survey_question
: This table stores questions in particular surveys. Each record represents a question (columnquestion_id
) in a survey (columnsurvey_id
). Question order is preserved using field line (columnline
). Questions can also be marked required (columnrequired
). -
survey_section_question
: This table stores sections in particular surveys. Each record represents a section (columnsection_id
) in a survey (columnsurvey_id
). Section can be under a question (columnparent_question_id
) or another section (columnsection_id
). -
survey_section_question
: This table stores questions in survey sections. Each record represents a question (columnquestion_id
) in a section (columnsurveys_section_id
). -
survey_status
: This defines statuses during answering of surveys, -
survey_text
: This table stores translatable columnsname
anddescription
.language
is also a column and each record has a value forname
in that language.survey_id
column links each record tosurvey
table. -
survey_section
: Each record in this tables represents a section in a survey. Content of sections are represented as local indices of questions in columnindices
. The name of the section is stored insection_text
table. -
section_text
: This table stores translatable columnname
which stores section name.language
is also a column and each record has a value forname
in that language.section_id
column links each record tosurvey_sectionnc
table. -
survey_section
: This table links surveys (columnsurvey_id
) to sections (columnsection_id
). Order of sections preserved using columnline
. -
user_assessment
: This stores an instance of an assessment (columnassessment_id
) for a paricular participant (columnuser_id
). -
user_assessment_answer
: This stores user answers (columnanswer_id
) for a particular assessment (coilumnuser_assessment_id
). -
user_audit
: This is an audit table for endpoints (columnendpoint
) that users (columnuser_id
) accessed. -
user_survey
: This table stores status of a survey for a participant. The status can bein-progress
orcompleted
.
Except account columns email
and password
in users table, none of the user facing columns ever overwrite a previous value and a history is always available. There are a few overwriting columns such as meta
in survey
table. These are mainly used for client level settings and do not contribute to any business logic.
There are no migrations for the project. The DB schema is created by sequelize.model.sync()
, triggered at application start and ./seed.js
You will need to install pakcer and terraform installed on your local machine.
Be sure to have your postgres host running and replace the survey_service_pg_host
value in the command below with the postgres host address. The command in 1.
below will allow you to build the AMI with default settings. You may also need to include additional environment variables in ./deploy/roles/api/templates/env.service.j2
before build.
- First validate the AMI with a command similar to
packer validate \ -var 'aws_access_key=my-aws-access-key' \ -var 'aws_secret_key=my-aws-secret-key' \ -var 'build_env=development' \ -var 'logstash_host=logstash.amida.com' \ -var 'service_name=amida_survey_microservice' \ -var 'ami_name=api-survey-service-boilerplate' \ -var 'node_env=development' \ -var 'jwt_secret=my-0-jwt-8-secret' \ -var 'pg_host=amid-survey-packer-test.czgzedfwgy7z.us-west-2.rds.amazonaws.com' \ -var 'pg_db=amida_survey' \ -var 'pg_user=amida_survey' \ -var 'pg_passwd=amida_survey' template.json
- If the validation from
1.
above succeeds, build the image by running the same command but replacingvalidate
withbuild
- In the AWS console you can test the build before deployment. To do this, launch an EC2 instance with the built image and visit the health-check endpoint at <host_address>:4000/api/health-check. Be sure to launch the instance with security groups that allow http access on the app port (currently 4000) and access from Postgres port of the data base. You should see an "OK" response.
- Enter
aws_access_key
andaws_secret_key
values in the vars.tf file - run
terraform plan
to validate config - run
terraform apply
to deploy - To get SNS Alarm notifications be sure that you are subscribed to SNS topic arn:aws:sns:us-west-2:844297601570:ops_team_alerts and you have confirmed subscription
Further details can be found in the deploy
directory.
Docker Compose:
docker-compose up
See the paper write-up for instructions on how to deploy with Kubernetes. The kubernetes.yml
file contains the deployment definition for the project.