Skip to content

Commit 6cfd63d

Browse files
authored
Merge pull request #1 from morissette/docs-contributing-md
Add CONTRIBUTING.md
2 parents abcc789 + 2130f51 commit 6cfd63d

1 file changed

Lines changed: 372 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
# Contributing to Benefit Decision Toolkit
2+
3+
Thank you for your interest in contributing! BDT is a volunteer-driven open source project run by [Code for Philly](https://codeforphilly.org/projects/dmn_benefit_toolbox-including_the_philly_property_tax_relief_screener). We welcome contributions of all kinds — code, documentation, bug reports, design feedback, and DMN eligibility rules.
4+
5+
## Table of Contents
6+
7+
- [Ways to Contribute](#ways-to-contribute)
8+
- [Project Structure](#project-structure)
9+
- [Development Environment Setup](#development-environment-setup)
10+
- [Running the Project](#running-the-project)
11+
- [Making Changes](#making-changes)
12+
- [Testing](#testing)
13+
- [Code Style](#code-style)
14+
- [Submitting a Pull Request](#submitting-a-pull-request)
15+
- [Working with DMN Files](#working-with-dmn-files)
16+
- [Deployment (Maintainers)](#deployment-maintainers)
17+
- [Getting Help](#getting-help)
18+
19+
---
20+
21+
## Ways to Contribute
22+
23+
- **Bug reports**: Open a GitHub issue with steps to reproduce
24+
- **Feature requests**: Open a GitHub issue describing the use case
25+
- **Code**: Fix bugs, implement features, improve tests
26+
- **Documentation**: Improve guides, fix typos, add examples
27+
- **DMN rules**: Add or improve eligibility rules in `library-api`
28+
- **Testing PRs**: Review and test open pull requests — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) (no local setup needed!)
29+
30+
---
31+
32+
## Project Structure
33+
34+
This is a monorepo with four distinct applications:
35+
36+
```
37+
benefit-decision-toolkit/
38+
├── library-api/ # Quarkus + Kogito: DMN files → REST APIs (Java 17)
39+
├── builder-api/ # Quarkus: Admin backend, Firebase integration (Java 21)
40+
├── builder-frontend/ # Solid.js: Admin UI + public screener interface
41+
├── website/ # Public project website
42+
├── docs/ # User and developer documentation
43+
├── e2e/ # Playwright end-to-end tests
44+
├── bin/ # Developer helper scripts
45+
├── .github/ # GitHub Actions workflows
46+
├── devbox.json # Development environment declaration (Nix)
47+
└── process-compose.yml # Multi-service orchestration for local dev
48+
```
49+
50+
**Important**: `library-api` uses **Java 17 + Quarkus 2.16.10** (required for Kogito), while `builder-api` uses **Java 21 + Quarkus 3.23.0**. Do not mix them up.
51+
52+
---
53+
54+
## Development Environment Setup
55+
56+
There are three ways to set up your environment. **Devbox is recommended** for local development.
57+
58+
### Option 1: GitHub Codespaces (Easiest)
59+
60+
No local installation required. Click the badge in the [README](README.md) to open a cloud-based development environment. Wait a few minutes for the first-time build to complete.
61+
62+
This is also a great way to test changes from a pull request — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md).
63+
64+
### Option 2: Devbox (Recommended for Local Development)
65+
66+
[Devbox](https://www.jetify.com/docs/devbox/) manages all project dependencies (Java 17, Java 21, Maven, Node.js 22, Firebase Tools, Bruno, etc.) using Nix, ensuring a consistent environment across machines.
67+
68+
```bash
69+
# Install Devbox + Nix and run one-time setup
70+
bin/install-devbox && devbox run setup
71+
```
72+
73+
**Tips**:
74+
- Install the [Devbox direnv integration](https://www.jetify.com/docs/devbox/ide-configuration/direnv) to auto-activate the Devbox shell when you `cd` into the project
75+
- VS Code users: install the [Devbox + Direnv extensions](https://www.jetify.com/docs/devbox/ide-configuration/vscode)
76+
- Customize your shell by editing `.devboxrc` (not committed to git)
77+
78+
### Option 3: Devcontainer (VS Code)
79+
80+
Open the project in VS Code, install the `Dev Containers` extension, and run **"Dev Containers: Open Folder in Container..."** from the command palette. The container includes the full Devbox environment. Expect a longer first-time build.
81+
82+
### Option 4: DIY (Not Recommended)
83+
84+
Manually install the dependencies listed in `devbox.json` (JDK 17, JDK 21, Maven, Node.js 22, Firebase Tools 14.27.0, Bruno, process-compose), then run:
85+
86+
```bash
87+
bin/setup
88+
```
89+
90+
---
91+
92+
## Running the Project
93+
94+
### Start All Services
95+
96+
```bash
97+
# Starts all 5 services in the correct dependency order
98+
devbox services up
99+
```
100+
101+
This orchestrates (in order):
102+
1. **Firebase Emulators** — Auth (9099), Firestore (8080), Storage (9199), UI (4000)
103+
2. **library-api** — REST API from DMN files at http://localhost:8083
104+
3. **sync-library-metadata** — Syncs library check metadata to Firebase emulators
105+
4. **builder-api** — Admin API (port configured via `QUARKUS_HTTP_PORT`)
106+
5. **builder-frontend** — Vite dev server at http://localhost:5173
107+
108+
> **Do not start services out of order.** `builder-api` depends on Firebase emulators being fully ready, and the metadata sync step must complete before `builder-api` starts.
109+
110+
### Running Individual Services
111+
112+
If you're only working on one part of the stack:
113+
114+
```bash
115+
# library-api only (no Firebase dependency)
116+
cd library-api && quarkus dev
117+
# Swagger UI: http://localhost:8083/q/swagger-ui
118+
119+
# Firebase emulators (required before builder-api)
120+
firebase emulators:start --project demo-bdt-dev --only auth,firestore,storage
121+
122+
# builder-api (after emulators are running)
123+
cd builder-api && quarkus dev
124+
125+
# builder-frontend (after builder-api is running)
126+
cd builder-frontend && npm run dev
127+
```
128+
129+
### Service URLs
130+
131+
| Service | URL |
132+
|---|---|
133+
| builder-frontend | http://localhost:5173 |
134+
| library-api | http://localhost:8083 |
135+
| library-api Swagger UI | http://localhost:8083/q/swagger-ui |
136+
| Firebase Emulator UI | http://localhost:4000 |
137+
138+
---
139+
140+
## Making Changes
141+
142+
### Branching
143+
144+
Create a feature branch from `main`:
145+
146+
```bash
147+
git checkout main
148+
git pull
149+
git checkout -b your-feature-name
150+
```
151+
152+
Use descriptive branch names, e.g. `fix-dmn-evaluation-edge-case` or `add-snap-eligibility-check`.
153+
154+
### Commits
155+
156+
Write clear, descriptive commit messages. Focus on *what* changed and *why*:
157+
158+
```
159+
# Good
160+
fix: handle null income value in eligibility check
161+
162+
# Good
163+
feat: add SNAP benefit eligibility check for Philadelphia
164+
165+
# Avoid
166+
fix stuff
167+
update
168+
```
169+
170+
### Syncing Library Metadata After library-api Changes
171+
172+
If you modify DMN files in `library-api`, re-sync metadata so `builder-api` picks up the changes:
173+
174+
```bash
175+
# Re-sync metadata
176+
./bin/library/sync-metadata
177+
178+
# Then restart builder-api (in the process-compose UI, restart the process)
179+
```
180+
181+
---
182+
183+
## Testing
184+
185+
### Java Unit Tests
186+
187+
```bash
188+
# builder-api
189+
cd builder-api && mvn test
190+
191+
# library-api
192+
cd library-api && mvn test
193+
```
194+
195+
These run automatically in CI on every push and pull request that touches `builder-api/` or `library-api/`.
196+
197+
### Bruno API Tests (library-api)
198+
199+
[Bruno](https://www.usebruno.com/) is used to test the DMN-generated REST endpoints in `library-api`. The test suite lives in `library-api/test/bdt/` and mirrors the DMN file structure.
200+
201+
```bash
202+
# Run all Bruno tests
203+
cd library-api/test/bdt && bru run
204+
```
205+
206+
**When adding a new benefit or check to `library-api`, add Bruno tests first** (test-driven development). Bruno tests validate DMN logic, while Java tests validate internal application behavior.
207+
208+
### End-to-End Tests (Playwright)
209+
210+
```bash
211+
# Run e2e tests (requires all services to be running)
212+
cd e2e && npx playwright test
213+
```
214+
215+
E2E tests run automatically in CI. They start all services via `devbox services up` and run against them.
216+
217+
### Before Submitting a PR
218+
219+
- [ ] Run Java unit tests for any service you modified
220+
- [ ] Run Bruno tests if you modified `library-api` DMN files
221+
- [ ] Verify your changes work end-to-end with all services running
222+
- [ ] Run Prettier on changed frontend files (see [Code Style](#code-style))
223+
224+
---
225+
226+
## Code Style
227+
228+
### JavaScript / TypeScript (builder-frontend)
229+
230+
The project uses [Prettier](https://prettier.io/) for formatting. Configuration is in `.prettierrc`:
231+
232+
```json
233+
{
234+
"semi": true,
235+
"singleQuote": false,
236+
"trailingComma": "all",
237+
"tabWidth": 2
238+
}
239+
```
240+
241+
Format changed files before committing:
242+
243+
```bash
244+
# Format a specific file
245+
npx prettier --write path/to/file.tsx
246+
247+
# Format all frontend files
248+
cd builder-frontend && npx prettier --write "src/**/*.{ts,tsx,js,jsx}"
249+
```
250+
251+
### Java (builder-api, library-api)
252+
253+
Follow standard Java conventions. There is no automated style enforcement beyond what Maven provides, so match the style of the surrounding code.
254+
255+
### DMN Files
256+
257+
- File names: `kebab-case.dmn`
258+
- Decision Service names: `{ModelName}Service` (e.g., a file named `snap-benefit.dmn` → Decision Service named `SnapBenefitService`)
259+
- Model names must be **globally unique** across the entire `library-api` — check existing names before adding a new DMN file
260+
- Imported decision services cannot share names even if in different models
261+
262+
---
263+
264+
## Submitting a Pull Request
265+
266+
1. Push your branch and open a PR against `main`
267+
2. Write a clear PR description: what changed, why, and how to test it
268+
3. All PRs require review from at least one maintainer (@Michael-Dratch or @prestoncabe — see [CODEOWNERS](.github/CODEOWNERS))
269+
4. CI runs API unit tests and E2E tests automatically — check that they pass
270+
5. For changes to `library-api` DMN files, include Bruno test results or a description of manual testing
271+
272+
**For non-technical contributors** testing a PR: follow the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) — no local setup needed.
273+
274+
---
275+
276+
## Working with DMN Files
277+
278+
DMN (Decision Model and Notation) is the core of this project. If you're new to DMN, start here:
279+
- [Learn DMN in 15 minutes](https://learn-dmn-in-15-minutes.com/)
280+
- [DMN Editor for VS Code](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension) (strongly recommended)
281+
282+
### Adding a Benefit to library-api
283+
284+
Benefits represent specific programs (e.g., SNAP, property tax relief). They define eligibility logic using DMN and automatically become REST endpoints.
285+
286+
1. **Write Bruno tests first** in `library-api/test/bdt/benefits/{jurisdiction}/{benefit-name}/`
287+
2. Create a DMN file: `library-api/src/main/resources/benefits/{jurisdiction}/{benefit-name}.dmn`
288+
3. Define a Decision Service named `{BenefitName}Service`
289+
4. Include a `checks` context and an `isEligible` boolean decision
290+
5. Save — Quarkus dev mode hot-reloads and the endpoint appears at `POST /api/v1/benefits/{jurisdiction}/{benefit-name}`
291+
6. Verify in Swagger UI: http://localhost:8083/q/swagger-ui
292+
7. Run your Bruno tests: `cd library-api/test/bdt && bru run`
293+
294+
### Adding a Reusable Check
295+
296+
Checks are reusable decision logic that can be shared across multiple benefits.
297+
298+
1. Create a DMN file: `library-api/src/main/resources/checks/{category}/{check-name}.dmn`
299+
2. Define a Decision Service named `{CheckName}Service`
300+
3. Standard inputs: `situation` (tSituation type), `parameters` (check-specific context)
301+
4. Standard output: `result` (boolean)
302+
5. Endpoint: `POST /api/v1/checks/{category}/{check-name}`
303+
304+
### Key DMN Constraints
305+
306+
- **Decision Service name must be `{ModelName}Service`** — this is required for automatic endpoint generation
307+
- **Model names are globally unique** — if two DMN files have the same model name, things break
308+
- Imported decision services cannot share names even across different files
309+
- Import `BDT.dmn` for shared types like `tSituation`
310+
311+
### Editing DMN Files
312+
313+
- Use the VS Code DMN Editor extension for visual editing
314+
- To see raw XML: right-click the file → "Reopen with Text Editor"
315+
- Changes are hot-reloaded in `quarkus dev` mode — no restart needed
316+
317+
---
318+
319+
## Deployment (Maintainers)
320+
321+
### library-api
322+
323+
Deployments are triggered by pushing a git tag matching `library-api-v*`. Use the provided script to create a release atomically:
324+
325+
```bash
326+
cd library-api
327+
328+
# Update pom.xml version and create git tag in one step
329+
./bin/tag-release 0.3.0
330+
331+
# Review the changes
332+
git show
333+
334+
# Push the commit and the tag (triggers GitHub Actions deployment)
335+
git push origin <your-branch>
336+
git push origin library-api-v0.3.0
337+
```
338+
339+
The GitHub Actions workflow validates that the tag version matches `pom.xml`, builds the Docker image, pushes to Google Artifact Registry, deploys to Cloud Run, and syncs library metadata automatically.
340+
341+
**Version source of truth**: `pom.xml`. The git tag, Docker image tag, and Cloud Run revision name all derive from it.
342+
343+
### builder-api
344+
345+
Deploys automatically to Cloud Run on every push to `main` that touches `builder-api/` files. No manual versioning needed.
346+
347+
### builder-frontend and docs
348+
349+
Deploy automatically to Firebase Hosting on push to `main`.
350+
351+
### Production Library Metadata Sync (Manual)
352+
353+
If you need to sync library metadata to production outside of a `library-api` deployment:
354+
355+
```bash
356+
export LIBRARY_API_BASE_URL=https://library-api-1034049717668.us-central1.run.app
357+
unset FIRESTORE_EMULATOR_HOST
358+
unset GCS_BUCKET_NAME
359+
unset QUARKUS_GOOGLE_CLOUD_STORAGE_HOST_OVERRIDE
360+
gcloud auth application-default login
361+
./bin/library/sync-metadata
362+
```
363+
364+
---
365+
366+
## Getting Help
367+
368+
- **GitHub Issues**: For bugs and feature requests
369+
- **Code for Philly**: Join [codeforphilly.org](https://codeforphilly.org) to connect with the team
370+
- **Project maintainers**: @Michael-Dratch and @prestoncabe
371+
372+
If you're stuck on setup, open an issue — improving the onboarding experience is itself a valuable contribution.

0 commit comments

Comments
 (0)