The code in this repo is deployed on Amazon's serverless platform, using Lambda, Kinesis, DynamoDB, among other things. This was our first substantial experience writing a serverless app, and we were pleasantly surprised by how quickly we could create a useful app with well-defined, decoupled components. If you're curious about building serverless event-driven applications, have a look around this repo and copy what you'd like.
The StopCOVID backend is serverless and event-driven. The core of the system is a Lambda function that receives commands from a Kinesis stream and produces events to a DynamoDB table and stream. Consumers of the event stream respond to the user and track per-user drill progress.
A simplified overview of the StopCOVID architecture.
For a lot more detail, see the architecture overview.
You can simulate the core of dialog processing on the command line — by feeding the dialog engine with command-line entries rather than entries from a kinesis stream. Try it out by running python simulator.py
.
The heart of the system is in engine.py
and, in particular, the process_command()
function. Here you'll see how we process commands and churn out a series of events in response.
We aggressively adopted type checking. We used Python type hints wherever we could and we used the pyright type checker to enforce type hints. We also used Marshmallow schemas for everything that we serialized to or deserialized from JSON.
The manage.py
script contains commands that we've found helpful while operating the Dialog Engine in production. You'll need appropriate AWS credentials in your environment to use this script. Type python manage.py --help
for info on what this script can do.
We use black for code formatting and flake8 for linting, with a custom rule setting maximum line length to 100.
black --config black.toml .
flake8
- Run
docker-compose up
in thedb_local
directory python -m unittest
To run dialog-engine
(and scadmin
) against local, mocked AWS infrastructure, we use localstack.
- Start localstack. This README & config assumes you'll run it in host mode on your machine (by passing the
--host
option), not using Docker. Make sure to include the configuration in.locastack-env
. e.g.:
pip install localstack
set -a
. ./.localstack-env
set +a
localstack start --host
-
Install serverless dependencies with
npm i
-
npm run deploy
-
In
scadmin
, ensure theBOTO_ENDPOINT_URL
env var is set
- Running in host mode requires Java 8+--which you must install yourself--for DynamoDB to run
- When restarting localstack in host mode, Kinesis/DynamoDB will sometimes end up in a corrupted state and/or fail to shut down, leaving lingering processes & blocked ports. Usually, manually stopping any
java
orkinesis-*
processes will fix this, or removing the localstackinfra
directory (more details in this GitHub issue) - At time of writing, localstack Kinesis directly invokes Lambda functions (if they are linked to a stream) before returning a response to PUT record requests. If there are errors in the lambda invocation, this can result in confusing "timeouts" in the client PUTing records to Kinesis
- The
start
npm script should work in theory, but is currently broken in practice: Cloudformation updates in localstack "fail" to deploy our lambdas (even though they successfully provision), so thedeploy
script always has an exit code of 1
After deploying dialog-engine
to localstack, sms_client.py
allows for direct interaction with dialog-engine
as if actually sending & receiving SMS from a client application. Example steps:
-
Start the
api
,dbq_consumer
, anddialog_event_consumer
docker-compose services inscadmin
-
Start the client, e.g.:
python sms_client.py http://localhost:4566 +15552345678
-
Invite an employee at the same phone from step 2 using a locally running
dashboard
instance -
You should receive an invitation in your step 2 shell; send a message back