# RTI Notifications Worker

Notifications-worker is an AWS Lambda function - deployed as a Docker image -
that is hooked into S3 bucket write events. Delphi runs timed jobs as they
ingest new data from external sources. The result of these runs is stored into
S3 as a multi-line JSON file (one JSON document per line).

As new files get generated, the notifications-worker reads the files, and
determines which devices should receive notifications. The new files are
received as AWS SQS notifications that have the S3 bucket and key (file) in
the payload.

First use case for notifications is to notify subscribed devices about
favorited tracks that reach certain streaming thresholds. The thresholds are
defined within Delphi's cron job.

The worker uses Auth0 management API to ensure that subscribed devices do not
receive notifications about content the users might not have access to (but to
which they may have subscribed to earlier when having access). Worker also
ensures that events are sent at most once to a given device by relying on a
UUID field provided by Delphi. A UUID is attached to each JSON document. This
UUID is stored, along with rest of the notification data, into the user
preferences DB prior to trying to send the data to Expo. Expo API ultimately
delivers the notifications to the mobile devices.

## System diagram

```
┌───────┐             ┌─────────────────┐
│  RTI  ├─────────────► ama-backend     │
└───▲───┘ 1) Favorite └───────┬─────────┘
    │        stored to        │
    │        DB       ┌───────▼───────────┐ ┌────┐
    │                 │ user-preferences  ├─► DB │
    │                 └─┬──────────────┬──┘ └────┘
    │                   │              │
┌───┴───────┐           │    ┌─────────▼─────────┐
│ Expo      │           │    │ Apollo Proxy      │
└───▲───────┘           │    └─────────┬─────────┘
    │                   │              │
    │ 5) Send push-     │    ┌─────────▼─────────┐
    │    notifications  │    │ Delphi Batch job  │ 2) ETL cron read
    │    to Expo API    │    └─────────┬─────────┘    favorites from
    │                   │              │              RTI database.
┌───┴───────────────────▼─┐  ┌─────────▼─────────┐    Write events
│            3) S3 event  ◄──┤ AWS S3            │    to S3.
│                         │  └───────────────────┘
│ 4) Fetch favorites      │
│    match S3 events      │  ┌───────────────────┐
│    cross-check access   ◄──┤ delphi-api        │
│                         │  └───────────────────┘
│                         │
│ AWS Lambda              │  ┌───────────────────┐
│ rti-notifications-wrkr. ◄──┤ Auth0             │
└─────────────────────────┘  └───────────────────┘
```

(Above diagram drawn using [asciiflow.com](https://asciiflow.com))

## Development

### Setup

The following setup is needed for the end-to-end local development.

Initiate AWS session (needs to be re-run every 12 hours)

    cd rti-user-preferences-service
    ./aws-set-session-token.bash aws-config-profile aws-credentials-user 12345

Ensure your AWS role has now access to the test file you intend to use for testing (see later steps)

    # Dump the test file on screen
    aws s3 cp s3://dev-artistapp-notifications/event=milestone/version=1/date=20210304/hour=14/1614869968914_1.json -

Start `rti-user-preferences-service`

    cd rti-user-preferences-service
    # nvm + npm
    npm run watch
    # in another shell
    npm run db:migrate

Start `ama-backend`

    cd ama-backend
    # nvm + npm
    npm run watch

Start `ama-frontend`

    cd ama-frontend
    # nvm + npm + expo
    npm run start:dev

Start `rti-notifications-worker`

    cd rti-notifications-worker
    npm run watch

Run tests

    cd rti-notifications-worker
    npm run test # unit tests
    npm run lambda:invoke '{}' # smoke test

### Triggering a notification

- Open the app
- Select an artist for whom you have a track ID available in the S3 file
  - To find test files, [check `dev-artistapp-notifications` S3 bucket](https://s3.console.aws.amazon.com/s3/buckets/dev-artistapp-notifications?prefix=event=milestone/version=1/)
- Go favorite that track
- Run the notification worker locally, pointing to the file that has an event for your favorited track with `npm run cli`
  - Run e.g. `npm run cli s3://dev-artistapp-notifications/event=milestone/version=1/date=20210304/hour=14/1614869968914_1.json`
  - You need to have an active AWS session, see "Setup"
- Notification should appear in the app

> Note: By default, deeplinks will not point towards your local development bundle.
> If you would like them to open in your development application, set EXPO_BUNDLE_HOST to the appropriate hostname and port

## Integration testing the lambda locally

This is how you emulate the lambda stack on your local machine.
It is useful when you want to test configuration changes, and make sure they will work in CI/CD and AWS.

Use docker CLI to build the container image locally

    npm run container:build

Start the container image locally using the Lambda Runtime Interface Emulator:

    npm run container:run

Now we can test function invocation with an empty event

    npm run lambda:invoke '{}'

or with a fake SQS event

    npm run lambda:invoke \
    '{"Records":[{"messageId":"19dd0b57-b21e-4ac1-bd88-01bbb068cb78","receiptHandle":"MessageReceiptHandle","body":"Hello from SQS!","attributes":{"ApproximateReceiveCount":"1","SentTimestamp":"1523232000000","SenderId":"123456789012","ApproximateFirstReceiveTimestamp":"1523232000001"},"messageAttributes":{},"md5OfBody":"{{{md5_of_body}}}","eventSource":"aws:sqs","eventSourceARN":"arn:aws:sqs:us-east-1:123456789012:MyQueue","awsRegion":"us-east-1"}]}'

## Testing

The test suite aims to mock and capture all I/O, including logging.

    npm run test
