Skip to main content

property-service — LOCAL_DEV_SETUP

Companion: DEPLOYMENT_TOPOLOGY · DATA_MODEL · API_CONTRACTS · TESTING_STRATEGY

This document is the binding "first 10 minutes" recipe for running property-service locally. The goal is: clone → pnpm idocker compose up → service responding on :3010 with seeded data, fully independent of cloud credentials.

Prerequisites: Node.js 20.x, pnpm 9.x, Docker 24+, GNU make. Windows users: use WSL2 for the docker compose stack.


1. Stack

docker-compose.dev.yml (lives in services/property-service/):

ContainerImagePortPurpose
pgpostgis/postgis:15-3.45432Cloud SQL surrogate; PostGIS preinstalled
redisredis:7-alpine6379Memorystore surrogate
pubsubgcr.io/google.com/cloudsdktool/cloud-sdk:emulators8085Pub/Sub emulator
wiremock-iamwiremock/wiremock8081iam-service JWKS + OPA bundle stubs
wiremock-fileswiremock/wiremock8082file-storage-service signed-URL + scan-event stubs
wiremock-aiwiremock/wiremock8083ai-orchestrator-service capability stubs
wiremock-geowiremock/wiremock8084geo-service geocoding stubs
mailpitaxllent/mailpit8025Captures any system notifications (audit emails)

The service itself runs on the host (pnpm dev) so file-watch is fast; the docker stack is only the dependencies.


2. Bootstrap

cd services/property-service
cp .env.example .env.development # already populated with localhost values
pnpm install
docker compose -f docker-compose.dev.yml up -d
pnpm db:migrate # runs all migrations against local pg
pnpm db:seed # loads the demo seed (next section)
pnpm dev # NestJS watch mode on :3010

Healthcheck:

curl -s http://localhost:3010/healthz # { "status": "ok" }
curl -s http://localhost:3010/readyz # { "status": "ready", "checks": {...} }

The dev JWT issuer (wiremock) signs tokens with a dev JWK; mint a dev token via:

pnpm run dev:jwt -- --tenant tnt_dev_kabul --user usr_dev_admin --roles tenant.admin
# → prints a token; export AUTH_TOKEN=<token>

3. Seed Data

pnpm db:seed produces a deterministic, ULID-stable dataset:

3.1 Tenants (provisioned in the dev tenant-service mock)

  • tnt_dev_kabul — Kabul, AF, locale ps-AF default + en, fa, currency AFN
  • tnt_dev_dushanbe — Dushanbe, TJ, locale tg-TJ default + ru, en, currency TJS
  • tnt_dev_tehran — Tehran, IR, locale fa-IR default + en, currency IRR

3.2 Properties (3, one per tenant)

Property IDTenantDisplay name (default locale)GeoStatus
ppt_dev_kabul_01tnt_dev_kabul"د کابل میلمستون" / "Kabul Melmastoon"(34.5553, 69.2075)published
ppt_dev_dushanbe_01tnt_dev_dushanbe"Меҳмонхонаи Душанбе"(38.5598, 68.7870)published
ppt_dev_tehran_01tnt_dev_tehran"مهمانخانه تهران"(35.6892, 51.3890)draft (intentionally — to exercise the publish flow)

Each published property has:

  • 5 room types (KING, TWIN, SUITE, FAMILY, DORM — Tehran has APT instead of DORM)
  • 10 rooms per property (30 total)
  • ≥ 1 hero photo + 4 supporting photos (synthetic small JPEGs in seeds/photos/)
  • Regional amenities seeded: wifi, breakfast, halal_kitchen, prayer_room, generator_backup, hot_water_24h (hot_water_scheduled for Dushanbe), women_only_floor (Kabul), airport_shuttle
  • Resolved policy: check-in 14:00, check-out 12:00, cancellation 24h_full_refund, child welcome, smoking no, pets no (dogs case_by_case for Tehran)
  • 1 OOO room per property to demonstrate the OOO/RTS flow
  • Tehran property is in draft to demonstrate publish invariants

3.3 Synthetic photos

Pre-built tiny JPEGs (1024×768, ~50 KB each) live in seeds/photos/ and are uploaded to the dev file-storage-service mock during seed; the mock returns verdict=clean after a 2 s delay.

3.4 Synthetic AI runs

pnpm db:seed:ai (optional) stages one accepted and one pending AI suggestion per property to demonstrate the HITL UI.


4. Common Commands

CommandPurpose
pnpm devNestJS watch mode
pnpm testUnit tests
pnpm test:intIntegration tests (Testcontainers spins its own pg/redis/pubsub)
pnpm test:e2eService-local E2E (uses the running docker compose)
pnpm linteslint
pnpm typechecktsc --noEmit
pnpm db:migrateApply migrations
pnpm db:migrate:downApply paired down.sql for the latest migration
pnpm db:seedLoad demo data
pnpm db:resetDrop schema + reseed
pnpm openapi:checkLint + breaking-change diff vs published spec
pnpm asyncapi:checkLint + schema-registry diff for events
pnpm contracts:verifyRun Pact provider verification against the local registry

Make targets mirror the most common ones (make dev, make test, make seed).


5. Mocked External Calls

  • JWT issuer / JWKS / OPA bundlewiremock-iam. Stubs in mocks/iam/. Includes a jwks.json, a sample OPA bundle, and a /v1/data/property/decisions endpoint.
  • File storage signed URLswiremock-files. The signed URL points back at wiremock-files itself, which "accepts" the upload and immediately publishes a media.asset.scanned.v1 to the Pub/Sub emulator with verdict=clean. To exercise the infected path, use pnpm dev:scan-infected <photoId>.
  • AI orchestratorwiremock-ai. Stubs for each capability return canned outputs derived from the input property to keep tests deterministic. Override per scenario in mocks/ai/scenarios/.
  • Geo servicewiremock-geo. High-confidence geocodes for the three seeded cities; low-confidence stub at address unknown street, nowhere to exercise AI fallback.

6. Useful URLs (running stack)


7. Tearing Down

docker compose -f docker-compose.dev.yml down -v # stops + removes volumes
pnpm db:reset # only needed if you want to keep the stack but reset data

8. Troubleshooting

SymptomLikely causeFix
pnpm dev exits with JWKS_FETCH_FAILEDwiremock-iam not updocker compose up -d wiremock-iam
Migrations stall on CREATE EXTENSION postgiswrong imageconfirm postgis/postgis:15-3.4
404 PROPERTY_NOT_FOUND after seedwrong tenant contextmint a fresh dev JWT with the matching tenant
Photos stuck uploadedwiremock-files scan stub offdocker compose restart wiremock-files
Pub/Sub emulator host not setenv not exported in shellexport PUBSUB_EMULATOR_HOST=localhost:8085
RLS surprise: empty listapp.tenant_id not setservice reset; ensure dev token includes tenant_id

When the dev environment differs from production (e.g., running without VPC connectivity), the service auto-detects via NODE_ENV !== 'production' and relaxes only the controls explicitly enumerated above.