fix(web,api): contrato estable por code para localización de errores 4xx#421
Open
nopestack wants to merge 5 commits into
Open
fix(web,api): contrato estable por code para localización de errores 4xx#421nopestack wants to merge 5 commits into
nopestack wants to merge 5 commits into
Conversation
…es 4xx Seguimiento de GlobalEmergency#338 (GlobalEmergency#348): la web matcheaba errores de dominio por substring/prefijo del `.message` en inglés, un acoplamiento frágil que degrada en silencio si el backend cambia la prosa y que los tests de `backend-error-messages.test.ts` no detectaban (hardcodeaban las mismas cadenas). Se añade un `code` estable a las excepciones de dominio detrás de los 12 patrones de KNOWN_BACKEND_ERRORS (SupplyLineValidationError, NeedResourceNotInEmergencyError, TargetNeedNotFoundError/ TargetNeedWrongEmergencyError, OfferItemsRequiredError, CapacityMustHaveWeightOrVolumeError, InvalidCapacityAmountError, InvalidCoverageError, InvalidCapacityWindowError) y se propaga en el body JSON de los filtros de excepción de needs/offers/logistics/resources/ supplies vía un helper compartido (`domainErrorResponseBody`). La web (`localizeBackendError`) ahora resuelve primero por `code` y sólo cae al matching por prosa como red de seguridad transicional (deploy-lag web/API) o para errores aún no migrados. Se añade también la cobertura de test que faltaba para `offer_items_required` en backend-error-messages.test.ts. La nota de UX opcional (limpiar el resaltado de fila inválida en el primer onChange tras un error) se deja fuera: toca el estado independiente de ~6 formularios distintos, no es un cambio trivial.
|
@nopestack is attempting to deploy a commit to the GlobalEmergency Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #348
Contexto
Notas de la review de #338 (mergeado, cerró #296):
localizeBackendError(
apps/web/src/lib/backend-error-messages.ts) mapea errores del backend acopy localizado matcheando por substring/prefijo del
.messageen inglés delas excepciones de dominio. Funciona hoy, pero el contrato es texto libre sin
nada que enlace
apps/apiyapps/web: si alguien reescribe un.messageen el backend, la web degrada en silencio al mensaje genérico, y los tests
existentes hardcodeaban las mismas cadenas por lo que no detectaban el
drift.
Qué opción se implementó
Se implementó el fix real (opción preferida del issue), no el mínimo
intermedio: expongo un
codeestable en las excepciones de dominio y lodevuelvo en el body de los filtros de excepción NestJS.
Descarté el mínimo intermedio (test que importa las clases reales y afirma
que su
.messagesigue casando cada patrón) porque, tras localizar las 12clases detrás de los patrones de
KNOWN_BACKEND_ERRORS, el alcance realresultó acotado: son sólo 9 clases de error, concentradas en 5 contextos
(
needs,offers,logistics,resources,supplies) máspackages/warehouse-core— no hizo falta tocaridentity,groups,organizations,volunteers,accreditation,templates,notificationsni el resto de los ~150 errores de dominio del repo. Conese alcance, el fix real era viable sin convertirse en un PR gigante.
Cambios
packages/warehouse-core:SupplyLineValidationError,CapacityMustHaveWeightOrVolumeError,InvalidCapacityAmountError,InvalidCoverageError,InvalidCapacityWindowErrorahora exponen.code. Para las clases con más de un mensaje posible(
SupplyLineValidationError,InvalidCapacityWindowError), elcodesepasa en el punto de
throw;InvalidCoverageErrorlo deja opcionalporque sólo uno de sus 5 sitios de
throwtiene copy en la web hoy — elresto sigue degradando al fallback genérico sin romper nada.
apps/api:NeedResourceNotInEmergencyError,TargetNeedNotFoundError,TargetNeedWrongEmergencyError,OfferItemsRequiredErrorexponen.code. Nuevo helper compartidoapps/api/src/shared/http/domain-error-response.ts(
domainErrorResponseBody) usado por los 5 filtros de excepciónafectados (
needs,offers,logistics,resources,supplies):añade
codeal body sólo cuando la excepción lo expone, así que siguesiendo backward compatible con
{ statusCode, message }.apps/web:localizeBackendErrorahora resuelve primero porcode(match directo
t[code]) y sólo cae al matching por prosa (la tablaKNOWN_BACKEND_ERRORS) como red de seguridad transicional — útil durantela ventana de deploy-lag entre web y API (despliegan por separado) y para
cualquier error que aún no haya migrado a
code. La firma cambió derecibir el
.messageya extraído a recibir elerrorcompleto delcliente tipado, lo que además simplificó los 5 call-sites
(
peticion,donar/ofrecer,registrar,voluntario,ofrecer-transporte) quitándoles la extracción manual derawMessage.offer_items_requireden
backend-error-messages.test.ts, más tests nuevos para el camino porcode, el fallback por prosa, y elcodede cada excepción de dominiotocada (en
apps/apiypackages/warehouse-core).No se tocó
packages/api-client/src/schema.ts: no hay decoradores@ApiResponsedocumentando el shape de error en estos endpoints, así quepnpm gen:apino produce diff (verificado).Nota UX opcional del issue — diferida
El punto opcional de limpiar el resaltado de fila inválida
(
InventoryField/SupplyLineList) en el primeronChangetras un errorno se incluyó: el estado
invalidRowse maneja de forma independiente en~6 formularios distintos (
peticion,registrar,pre-registro,create-shipment,inventory-edit-form,record-inventory-entry), asíque no es un cambio trivial de un solo punto. Queda fuera de este PR.
Gate local
pnpm install --frozen-lockfilepnpm --filter '@globalemergency/warehouse-core' build && test(213 tests)pnpm --filter '@globalemergency/warehouse-postgres' buildpnpm --filter api buildpnpm --filter api exec eslint "{src,apps,libs,test}/**/*.ts" --max-warnings=0pnpm --filter api exec prettier --check "src/**/*.ts" "test/**/*.ts"pnpm --filter api test(259 suites / 1654 tests)pnpm --filter @reliefhub/api-client build && pnpm --filter web buildpnpm --filter web lintpnpm --filter web test(122 tests)pnpm gen:api→ sin diff enschema.ts/openapi.jsonTodo en verde.