Exhale

Exhale was once Palolo.

CI

  1. Run Initial Setup if you haven't already
  2. To run the app locally:
x go

Note: The x command lives in the /scripts directory in this repo. To run the command from anywhere, run scripts/x install.

By default, the app is accessible at http://localhost:3000. Our dev server is actually two processes. Running on port 3000 is the Vite dev server, which serves our client-side JavaScript and handles hot-module reloading. The Vite dev server proxies API requests to http://localhost:3001, which is where the actual API server lives in development.

If you only need to run the API server (but you don't need the browser client), you can just run:

pnpm run --dir packages/server dev

Using the VS Code Debugger

Server: Make sure you are running x go. In the VS Code debug panel, run "Attach to Server". (Alternatively, quit any x go process. In the VS Code debug panel, run "Launch Server".)

Client: Make sure you are running x go. In the VS Code debug panel, run "Launch Chrome".

In both scenarios, you should be able to add breakpoints in TypeScript files via the editor.

To get the database in sync with upstream changes

We favor resetting your database on an upstream change. To reset and reseed your database, run:

x db reset

To run migrations without resetting the database:

x db migrate

Creating new migrations

If the set of migrations is out of date with the latest models.prisma file, run the same commands as above to generate a new migration. Prisma will prompt you to enter a name for the new migration and always prepends a timestamp to all migration names. Docs here.

x db migrate

Sometimes you'll create a bunch of migrations in a row as you are working on a branch. Whenever possible, flatten those migrations into a single migration before merging:

cd packages/server
rm -rm prisma/migrations/<your migrations here>
x db migrate

Managing dependencies

If the dependencies have changed upstream, run:

x update

To manage dependencies, cd into a specific package directory and use pnpm add, pnpm update, or pnpm remove.

Initial Setup

Complete Engineering Technical Onboarding before continuing.

Bash/Zsh Setup

Edit your .zshrc to include these lines

# PALOLO_ROOT should provide a path to where you have cloned the exhale project repo.
export PALOLO_ROOT=~/development/palolo
source $PALOLO_ROOT/utils/.zshrc

In addition, run scripts/x install to make the x command globally accessible.

Install our dependencies

Get Homebrew if you don't have it already.

By default, we have a script to do this for you

# Runs an `aws configure` which requires manual inputs
# So wait until you're past that  before grabbing that cup of coffee
scripts/osx_dependencies.sh

# Bare metal
scripts/x install
x init
x go
# Now open localhost:3000 in your browser

# Docker
make down docker-build up
# Now open localhost:3002 in your browser

# Completely clean docker build
make down clean-db docker-build-clean up

AWS setup requires manual inputs, so keep an eye on it while it runs.

If you need to do parts of this manually:

Configure AWS CLI

brew install awscli

You will manage your AWS authenticated session using leapp. see AWS SSO with Leapp

Install Node 16

brew install node@16
brew link node@16 # It's "keg-only" by default.

Install pnpm

npm -g install pnpm

pnpm is an alternative Node.js package manager. We use it because it works better with monorepos, it helps us avoid accidental dependency imports by only exposing our direct dependencies in the top level of node_modules, and it doesn't automatically install peer dependencies.

Alternatively, install pnpm using one of the standalone methods on this page, then run pnpm env use --global 16 to install Node 16.

Install Postgres 14

brew install postgresql

If we're still on Postgres 14 and Postgres 15 gets released, this will probably change to:

# Unnecessary right now.
brew install postgresql@14
brew link postgresql@14

Install Node dependencies with pnpm

Within the root palolo.com directory, run:

pnpm install --frozen-lockfile

You can ignore the following peer dependency complaints:

Install Localstack and run local AWS services

We use localstack to run a local instance of SQS when developing.

python3 -m pip install localstack
python3 -m pip install awscli-local

Currently the only AWS service that local development depends on in SQS. Create the expected queues:

cd packages/server
pnpm run queue init

Set local environment variables

x secrets

This will generate an .env containing relevant secrets and other environment variables.

If you'd like to make changes to this file, put them in local.env and re-run the secrets command. They will be appended to the end. local.env is ignored by git.

If you'd like to manage secrets, visit the AWS Secrets Manager. Be sure to tag new secrets to an environment or environments; see pnpm run secrets --help for more information.

Initialize local database

First, initialize the local database.

x db init

Now get up to date with the latest migrations:

x db migrate

This should run all the migrations in the prisma/migrations directory, and regenerate the magic @prisma/client dependency.

Finally, seed the database with test data. This will create a variety of test organizations that are composed of uninvited, invited, KYC, linked but not KYC, or paycheck connected (PC) members only and some that consist of all of those types of members. For testing purposes, you can log in with the following emails using the password test:

Note: due to generating a random amount of members, for the non-exhale user options below, there is a higher likely hood a member will exist if it is a lower number. Take the ranges below with a grain of salt, it might be prone to change as time continues.

  1. [first name of Exhale employee]@palolo.com EX: giaco@palolo.com

    • This will log you in as an Exhale super user with unrestricted access to the test data.
  2. linked[number from 1 -> ~80+]@example.com EX: linked15@example.com

    • This will log you in as a linked member who has not KYCed yet.
  3. kyc[number from 1 -> ~80+]@example.com EX: kyc10@example.com

    • This will log you in as a KYC linked member.
  4. pc[number from 1 -> ~30+]@example.com EX: pc23@example.com

    • This will log you in as a pay connected member.
  5. admin[number from 1 -> ~15+]@example.com EX: admin12@example.com

    • This will log you in as a administrator to an organization.
    • The member you log-in as may either be uninvited, signed up, or signed up AND a participant.
x db seed

Running the script above will create a significant amount of data that will take time to generate. But sometimes during local development we do not want to generate a lot of members each time. To fix this you can add the following flags after x db seed in the command line before running.

  1. --empty: clears your database and does not fill it

NOTE: The following numbers will affect the amount of members in the Garden Cafe organization

  1. --tiny: 13 members with random statuses
  2. --small: 53 members with random statuses
  3. --normal or just x db seed: 1003 members with random statuses

Explore local database

x db studio

This will load the Prisma studio where you can explore and edit data.

Continuous integration

We use Github Actions for CI; see .github/workflows/ci.yml for the relevant workflow.

If you are having trouble with a particular step of a workflow, try using the ssh step to open an SSH session so you can investigate the machine that the workflow breaking on. Add the following step immediately before the failing step:

- uses: ./.github/actions/ssh

In the actions log for the run the ssh command you need to use will be shown. Terminate the tmux session when you are ready to continue the workflow.

Resetting local dev environment

For a more drastic approach of simultaneously updating dependencies, updating secrets, and resetting the database of your local dev environment:

x reset

Testing

To run tests in the default test group, which is most of them (any *.spec.ts file):

pnpm run test

To run tests for a non-default group (such as for our Baas), run

PALOLO_TEST_GROUP=$group pnpm run test
# Or use the shortcuts defined in server/package.json
pnpm run test:solid
pnpm run test:noble

This will run tests in any *.spec.$group.ts file.

Testing Locally

To force a loan or KYC check to be rejected, set the user's middle name to Reject. To force them to go into review, set it to Review.

Create direct deposits for a user:

pnpm run --dir packages/server baas direct-deposit 'user-id' 'direct deposit description'

Locally preview notifications (including emails):

cd packages/notifications
pnpm run dev

Then, open http://localhost:3005. To test another renderer, add it to the renderers list at the top of packages/notifications/src/local-preview.tsx.

Using localstack to send email locally:

  1. Ensure Docker is running
  2. pnpm run queue init inside of packages/server
  3. Ensure the domain you are sending to is in const allowedEmailDomains, or email will not be sent

You can reset the queue with pnpm run queue reset, but it shouldn't be necessary.

Send loan bill reminders:

Change NEXT_BILL_REMINDER_DIFFERENCES to [30], then:

pnpm run worker '{ "type": "PerkInstancesReminder", "queue": "Notifications", "data": {} }'

Docker

To push an image from local:

# Create a new builder that can do multi-architecture manifest natively
docker buildx create --use --name mybuild

# Push your image
EXPORT MY_TAG=<my deployment tag>
make DOCKER_ARGS="--platform=linux/arm64,linux/amd64 --output=type=registry" DOCKER_VERSION="${MY_TAG}" docker-build  # docker-build-app/postgres/whatever

# Revert to local docker
docker buildx use default

To smoke-test changes to Dockerfile or other related scripts (e.g. docker_base_setup.sh):

# Build the image locally.
make docker-build-clean

# Bring it up.
make down && make up

# Test it out! It should be running at http://localhost:3002

Webhooks

To test webhooks (and emails) locally, first setup ngrok or an equivalent service to get a publicly-accessible URL that routes to your local server. Once you have that running, you should configure it with the service you want to test. In order to ensure all URLs generated by Exhale use the publicly-accessible URL, be sure to set PUBLIC_PALOLO_DOMAIN=<ngrok-url> in your environment.

# Start ngrok
ngrok http 3000

Solid

To test Solid webhooks, use the following to create a webhook that points at ngrok:

SOLID_WEBHOOK_URL=<ngrok-url> pnpm run baas

That will create a webhook that you can find the Solid dashboard. Retrieve the Solid webhook secret key and start your dev server:

SOLID_WEBHOOK_SECRET_KEY=<key> pnpm run dev

You don't strictly need to provide the webhook secret locally, but we will need to provide it in production.