Files
task-stream/docs/06-desarrollo.md
Josep Piguillem 6c3550ae9d docs(auth): two Pireon clients, production URLs and sign-in troubleshooting
- Registering clients: the authorization_code client is confidential and needs
  its secret; Pireon shows it once (rotate-secret if lost); containers must be
  recreated to read new environment variables.
- Production URLs (taskstream.pireon.dev, api.taskstream.pireon.dev) and their
  redirect URIs.
- "Si el login falla": each error seen while deploying (not configured,
  unauthorized_client, missing client_secret, invalid_client, redirect URI,
  expired code, wrong credentials, intermittent 404s) with cause and fix.
- READMEs mention both clients; Word/PDF regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:01:33 +02:00

96 lines
7.1 KiB
Markdown

# 6. Desarrollo
## 6.1 Requisitos
- Node 22 y npm 10.
- Docker (PostgreSQL, MongoDB y Redis).
- Acceso al Nexus de Pireon para `@pi/ui`. El scope está en `.npmrc` (sin token); el token va en tu `~/.npmrc`:
```sh
npm config set //nexus.pireon.dev/repository/npm-releases/:_authToken "<token>"
```
## 6.2 Puesta en marcha
```sh
npm install
docker compose up -d postgres mongo redis
cp .env.example .env # ajusta PIREON_*
npx nx serve api # http://localhost:3000 (GraphiQL en /graphql)
npx nx serve web # http://localhost:4200
```
`nx serve web` usa `apps/web/proxy.conf.json` para enviar `/api` y `/graphql` (también el WebSocket) a la API, así que
la web y la API comparten origen igual que en Docker.
## 6.3 Comandos
| Comando | Qué hace |
| -------------------------------- | --------------------------------------------------------------------------------- |
| `npx nx run-many -t lint` | Lint de todo, incluidas las reglas de capas y contextos |
| `npx nx run-many -t test` | Tests unitarios (dominio y aplicación, API y web) |
| `npx nx e2e api` | Flujo completo de la API contra Docker, con Pireon simulado |
| `npx nx run-many -t build` | Builds de la API y la web |
| `npx nx run web:codegen` | Regenera los tipos GraphQL del cliente desde `apps/api/schema.gql` |
| `npx nx run api:prune` | Build de la API con `package.json` y lockfile podados (lo usa Docker) |
| `npx nx graph` | Grafo de dependencias entre proyectos |
| `npx nx test api-tracker-domain` | Tests de una librería concreta (`api-<contexto>-<capa>`, `web-<contexto>-<capa>`) |
### Tests
| Dónde | Qué se prueba | Herramienta |
| ----------------------------------- | ---------------------------------------------------------------------- | ------------------------ |
| `api/*/domain` | Reglas de los agregados y reconstrucción desde el historial de eventos | Jest |
| `api/*/application` | Commands contra un event store en memoria (incluida la concurrencia) | Jest + `@nestjs/testing` |
| `apps/api/e2e` | Todo el flujo: GraphQL → Postgres → Redis → Mongo → subscriptions | Jest contra Docker |
| `web/*/domain`, `web/*/application` | Sesión y reducers del tablero | Jest / Vitest |
| `apps/web` | Rutas y guards | Vitest |
El e2e sustituye a Pireon por usuarios fijos (`token-ana`, `token-bob`…) con `overrideProvider(IdentityProvider)`; el
resto de la pila es real.
## 6.4 Cómo añadir una funcionalidad
Ejemplo: **marcar un ticket como bloqueado con un motivo**. El recorrido es siempre el mismo, de dentro hacia fuera.
### API
1. **Dominio** (`libs/api/tracker/domain`)
- Evento `issue-blocked.event.ts` con `@AutowiredEvent`, que extiende `TrackerEvent`.
- En `Issue`, el método `block(actorId, reason)` valida y hace `this.apply(new IssueBlockedEvent(…))`; el handler
`onIssueBlockedEvent(event)` cambia el estado.
- Test en `issue.spec.ts`, incluida la reconstrucción desde el historial (`replay`).
2. **Aplicación** (`libs/api/tracker/application`)
- `commands/block-issue.command.ts` y `block-issue.command-handler.ts`: rehidratar (`WriteAccess`), llamar al
dominio y `commitAndWait()`. Registrar el handler en `command-handlers.ts`.
- Método en `IssuesService`.
- Proyección `event-handlers/issue-blocked.event-handler.ts` (`@EventsHandler`) que actualiza el read model y avisa
por `ReadModelNotifier`. Añadir el evento al `ActivityEventHandler` y una frase en `activity-summary.ts`.
- Si el read model cambia, añadir el campo a `IssueReadModel` (dominio) y al esquema Mongo (infraestructura).
3. **Presenters** (`libs/api/tracker/presenters`): input y mutation `blockIssue` en `IssueResolver`, que llama al service.
4. Arrancar la API para regenerar `apps/api/schema.gql`.
### Web
5. `npx nx run web:codegen` tras añadir la mutation a `issues.graphql`.
6. **Dominio**: método en el puerto `IssuesGateway`. **Infraestructura**: implementarlo en `GraphqlIssuesGateway`.
7. **Aplicación**: evento `blockRequested` en `issuePageEvents`, su efecto en `IssueStore` (`runCommand`) y el método
`block(reason)` en `IssueFacade`.
8. **Presenters**: botón y formulario en la página del ticket que llaman a `facade.block(reason)`.
El lint impide atajos: si un resolver intenta usar un repositorio o una página intenta usar Apollo, falla. Y como `application` solo
exporta facades, una página tampoco puede tocar un store ni despachar eventos por su cuenta.
## 6.5 Problemas conocidos
| Síntoma | Causa y solución |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `npm install` falla con `Cannot read properties of null (reading 'edgesOut')` | Bug de npm con un peer opcional de Vite que arrastra vitest 5. El `overrides` de `package.json` fija vitest; no lo quites |
| `401` al instalar `@pi/ui` | Falta el token del Nexus en `~/.npmrc` (o `NEXUS_TOKEN` al construir con Docker) |
| `failed to stat /root/.npmrc` al desplegar | Versión antigua del `docker-compose.yml`: ahora el token llega por `NEXUS_TOKEN` (ver [Despliegue](07-despliegue.md)) |
| `nx serve web` arranca en el puerto 3000 | Nx carga el `.env` raíz (`PORT=3000`) en todas las tareas; `apps/web/.env` lo fija en 4200 |
| La API no ve un evento nuevo al deserializar | La clase del evento no tiene `@AutowiredEvent` o no se importa |
| Un componente de `@pi/ui` lanza `Unable to find icon` | Falta registrar ese icono en `libs/web/shared/ui/src/lib/icons.ts` |
| Tablas antiguas (`projects`, `issues`, `outbox`) en tu Postgres local | Restos de una versión anterior sin event sourcing; `docker compose down -v` empieza de cero |
| El login con Pireon falla (`unauthorized_client`, `client_secret`…) | Configuración de los clientes OAuth: ver [Si el login falla](05-autenticacion.md#si-el-login-falla) |