# 🚩 Ejercicio 8 · La API que siempre dice que sí

**Clase 8 — APIs REST y consumo de servicios**
· Dificultad: 🟠 medio-alto · Tiempo estimado: 60–90 min

---

## La situación

La API de empleados del Liquidador funciona. Devuelve los datos correctos, no
tira errores, y si la probás con el navegador parece impecable.

Contesta **200 para todo**:

```
  ✔ GET    /api/empleados/1        200        200   el empleado existe
  ✘ GET    /api/empleados/999      200        404   ese empleado no existe
  ✘ POST   /api/empleados          200        201   alta válida: lo creó
  ✘ POST   /api/empleados          200        422   alta inválida: no lo crea
  ✘ DELETE /api/empleados/2        200        204   lo borró, nada que devolver
  ✘ DELETE /api/empleados/999      200        404   no se puede borrar lo que no está
```

Para una persona mirando la pantalla eso pasa desapercibido. Pero **del otro
lado de una API no hay una persona: hay un programa**, y ese programa no tiene
ojos. Lo único que tiene para saber qué pasó es el código de estado.

Una API que contesta 200 cuando el empleado no existe le está mintiendo a quien
la consume. El sistema de RRHH del otro lado va a guardar un empleado vacío y
nadie se va a enterar hasta el mes que viene.

## Lo que hace especial a este ejercicio

Consumir una API ajena es fácil **mientras la API ajena funcione**. El problema
es el otro día.

Este ejercicio trae un **cliente HTTP falso al que le podés provocar desgracias**
(`src/nucleo/ClienteHttp.php`). En cada corrida se prueba tu código contra cuatro
realidades:

```
  el servicio anda                        ✔ siguió andando   ok · $1.050,00
  el servicio no responde                 ✘ se cayó tu aplicación
  el servicio tarda una eternidad         ✘ se cayó tu aplicación
  contesta 200, pero manda cualquier cosa ✔ siguió andando   ⚠ tiró 1 warning
```

Eso es lo que no se puede ensayar con una API de verdad: no podés pedirle a un
tercero que se caiga un ratito para probar tu manejo de errores.

Mirá la última línea con atención. **Ese caso "anda"** —devuelve lo correcto— y
sin embargo el código está roto: PHP avisó y siguió de largo. Un warning en
producción no lo ve nadie.

## Tus cinco tareas

| # | Dónde | Qué |
|---|---|---|
| 1 | `app/Controllers/ApiEmpleadoController.php` | **404** cuando el empleado no existe |
| 2 | idem | **201** al crear · **422** cuando los datos no sirven (y no crear nada) |
| 3 | idem | **204** al borrar · **404** al borrar algo inexistente |
| 4 | `app/Recursos/EmpleadoResource.php` | Decidir **qué sale** por la API |
| 5 | `app/Servicios/CotizacionService.php` | Sobrevivir a que el tercero se caiga |

Las respuestas ya están armadas en `src/nucleo/Respuesta.php`: `ok`, `creado`,
`sinContenido`, `noEncontrado`, `noProcesable`. Tu trabajo es elegir la correcta.

## Cómo correrlo

```bash
docker compose run --rm ejercicio
```

Con los cinco pasos en verde aparece tu bandera `ipap{c08-xxxxxxxxxx}`, que
—como siempre— **no está escrita en ningún archivo**.

> **Sin Docker:** `cd src && php index.php`

## Lo que vas a practicar

| Paso | Concepto de la clase |
|------|----------------------|
| 1–3 | **Códigos de estado HTTP** y verbos: el contrato de una API REST |
| 2 | **Validación en APIs**: 422 y el cuerpo con los errores |
| 4 | **API Resources**: separar el esquema de la base del contrato público |
| 5 | **HTTP Client**: timeout, manejo de errores y degradación elegante |

## Tres cosas que el verificador no te deja hacer

**Contestar siempre lo mismo.** Cada paso prueba el caso que tiene que salir
bien *y* el que tiene que fallar. Devolver 404 siempre no es "manejar el 404":
rompe el caso feliz. Por eso ningún paso se decide con una sola petición.

**Avisar del error después de haber hecho el desastre.** El paso 2 cuenta
cuántos empleados quedaron en la base. Validar, contestar 422 y guardar igual
es un error muy común y desde afuera no se nota.

**Andar "de casualidad".** El paso 5 cuenta los **warnings** que emite PHP.
Recorrer un `null` no explota: PHP avisa y sigue, así que el resultado final
puede ser correcto con el código roto. Lo que delata al código que no desconfía
de la respuesta ajena no es lo que devuelve, es el ruido que hace en el camino.

## Si te trabás

1. **Los códigos.** Regla corta: `2xx` salió bien · `4xx` te equivocaste vos
   (el que llama) · `5xx` me equivoqué yo (el servidor). `404` no es un error
   del servidor: es una respuesta perfectamente normal.
2. **El 204.** Significa literalmente "no content". Si mandás un cuerpo, te
   estás contradiciendo. Es la respuesta natural de un `DELETE`.
3. **El Resource.** Que un campo esté en la tabla no significa que tenga que
   salir. Leé el comentario del archivo: pide cuatro claves exactas, y una de
   ellas **cambia de nombre** respecto de la base. El orden no importa.
4. **La cotización.** Son tres defensas distintas y hacen falta las tres:
   el `timeout`, el `try/catch` y desconfiar de lo que llegó. Una API caída
   y una API que contesta basura son problemas distintos.
5. **El timeout.** Que sean **3 segundos**, como dice el comentario.

---

## Lo interesante

Fijate en el orden de gravedad de las tres fallas del punto 5.

Que el tercero **se caiga** es lo más obvio y lo menos peligroso: explota fuerte,
te enterás enseguida, lo arreglás. Que **tarde** es peor, porque tu servidor se
queda esperando con la conexión ocupada, y con suficiente tráfico un servicio
lento tira más sistemas que uno caído. Y que conteste **basura con un 200** es lo
más difícil de todo, porque tu código sigue andando y los datos malos entran
sin que nada proteste.

Por eso la degradación elegante no es un lujo: la pantalla de recibos **no
necesita** el dólar. Que se caiga entera porque una API ajena estornudó es una
decisión de diseño, aunque nadie la haya tomado conscientemente.

> En Laravel esto se escribe casi igual:
> `Http::timeout(3)->retry(2, 100)->get($url)` dentro de un `try`, y `->failed()`
> o `->successful()` para mirar el resultado. Lo que cambia es la sintaxis; el
> criterio de qué hacer cuando el otro falla lo seguís poniendo vos.
