The alerts server API is built relaying on the following principal technologies:
- Scala.
- PostgreSQL.
- Play Framework.
- Anorm.
- Scalactic.
- Mailgun - for sending emails.
- Docker - for testing only.
- docker-it-scala - for testing only.
At the moment it is supporting the following currency exchanges, more exchanges might be added in the future:
The following alerts are supported while more alert types should be added soon:
- Fixed price - Receive a notification when a currency gets above or below the given price, you can set a base price to receive a more detailed message.
- Daily price - Receive a daily notification with the price of the currencies of your choice (currently disabled).
- New currencies - Receive a notification when a currency is addded to the exchanges of your choice.
Execute sbt compile
command for compiling the application.
In order to run the application locally, you will need the following dependencies:
- A PostgreSQL instance (strictly necessary).
- A Mailgun API key (necessary for sending emails).
There are some environment specific configuration values that you need to set to run the application successfully, the recommended way is to create a file .env
(which is ignored by git to avoid you pushing sensitive data by accident)), then take the following content and update it according to your needs:
# database
export CRYPTO_COIN_ALERTS_PSQL_HOST="localhost:5432"
export CRYPTO_COIN_ALERTS_PSQL_DATABASE="crypto_coin_alerts"
export CRYPTO_COIN_ALERTS_PSQL_USERNAME="postgres"
export CRYPTO_COIN_ALERTS_PSQL_PASSWORD="password"
# from mailgun
export MAILGUN_API_SECRET_KEY="REPLACE_ME"
export MAILGUN_DOMAIN="www.cryptocoinalerts.net"
Last, as the development environment is prepared for running without nginx, uncomment the line with this text play.filters.enabled += "play.filters.cors.CORSFilter"
(or just add the text as a new line) from application.conf.
Execute source .env; sbt run
command for running the application.
A more flexible way would be to modify application.conf file to your needs and then execute sbt run
command.
In order to run the tests execute sbt test
command, note that Database tests depend on docker to run.
Most of the tests belong to one of these categories:
- Simple tests.
- Database tests
- API tests.
A simple test is like a relaxed unit test, it tests a single method without mocking external libraries, for example, see JWTServiceSpec.
A database test is basically an integration test that ensures that a data handler is working as expected, it uses a real PostgreSQL instance provided by Docker with the help of docker-it-scala library.
The postgres image is created before running the test, we apply all the play evolutions to have the schema updated, and the image is destroyed after running the test.
All of these tests are extending PostgresDataHandlerSpec, for example, see UserPostgresDataHandlerSpec.
An API test is similar to an integration test because it is testing the whole system integration swapping external dependencies for internal ones:
- PostgreSQL is replaced by in-memory data implementations (see UserInMemoryDataHandler).
- External services are replaced by a faked version (see FakeEmailService).
These tests are useful for verifying that the API works as expected on the client side, they intention is to cover the use cases that are going to be used on the clients consuming the API.
Here are some details about the architecure design, they could be useful if you want to understand the code.
We use the playsonify extensively, you are encouraged to read its documentation.
When working with non-blocking results, the code could get an awful level of nesting while computing a result using several steps, we use a simple monad transformed called FutureOr), you could see its usage at UserService#create().
There are several components that are configurable, as we already are using play framework, it is very simple to integrate the typesafe-config which loads the configuration from the application.conf
file (hopefully we will switch to more typesafe configuration library in the future).
We are creating a base trait that could be easily extended for testing, and implementing the config depending on the Configuration
from play framework, note that no sensitive information should be stored in the configuration files that are being tracked by git, a better approach is to declare them as environment variables or Java system variables.
See JWTConfig.
For building the HTTP API, we use the controllers package, the controller classes make extensive use of the AbstractJsonController (from playsonify), it help us to build a controllers that receives JSON and produces JSON (it handles application errors as well).
The controllers could receive a model (that should be deserializable using play-json) and produce model (that is serializable using play-json).
A controller responsibility is very simple:
- Deserialize the input (if any).
- Delegate the action to the proper service.
- Serialize the result using a response HTTP Status that makes sense for the action.
See UsersController.
The services package is the one containing most application logic and rules, the controllers depend specifically on this package for performing most actions and retrieving data.