10 KiB
07 — API y contratos TypeScript
Endpoints actuales confirmados
Todos los paths son reales y relativos al servidor; no se introduce ninguno en esta auditoría. Params y cuerpos se validan con schemas Zod. Las rutas locales reciben actor de sesión para escribir; borrar no crea una revisión de Resource. routes: resource.routes.ts:54 catalogRoutes: resource-catalog.routes.ts:65 softwareHTTP: software-installation.routes.ts:116 applicationHTTP: application-configuration.routes.ts:116.
| Método y ruta | Entrada | Respuesta / función |
|---|---|---|
| GET /api/resources/:companyId | InventoryParamsSchema; query environmentId UUID opcional | InventoryResponseSchema: resources[], references?; listado completo |
| GET /api/resources/:companyId/network-share-diagnostics | Compañía; query vacía | items de diagnóstico SMB |
| POST /api/resources/:companyId | SaveResourceRequestSchema {values} | ResourceResponseSchema, 201 |
| POST /api/resources/:companyId/:id/update | ResourceParamsSchema + {values} | ResourceResponseSchema, 200 |
| POST /api/resources/:companyId/:id/status | {active:boolean}; query vacía | ResourceResponseSchema |
| POST /api/resources/:companyId/:id/delete | {} y query vacía | DeleteResourceResponseSchema {deleted:true} |
| GET /api/resource-catalog/:scope | scope global o UUID; query vacía | CatalogResponseSchema items de definiciones |
| POST /api/resource-catalog/:scope | {values:ResourceDefinitionSchema} | SaveDefinitionResponseSchema, savedId, 201 |
| POST /api/resource-catalog/:scope/:id/update | Igual, con ID | SaveDefinitionResponseSchema, 200 |
| POST /api/resource-catalog/:scope/:id/status | {active:boolean o null} | Catálogo actualizado; null hereda en override local |
| GET /api/software-installations/:companyId | SoftwareParamsSchema; query vacía | configurations[], incluye needsReview/history |
| POST /api/software-installations/:companyId | {values:SoftwareInstallationInputSchema} | configuration, 201 |
| POST /api/software-installations/:companyId/:id/update | Igual, SoftwareItemParamsSchema | configuration, 200 |
| POST /api/software-installations/:companyId/:id/delete | {} | {deleted:true} |
| GET /api/application-configurations/:companyId | ApplicationParamsSchema; query vacía | configurations[], incluye needsReview/history |
| POST /api/application-configurations/:companyId | {values:ApplicationConfigurationInputSchema} | configuration, 201 |
| POST /api/application-configurations/:companyId/:id/update | Igual con ApplicationItemParamsSchema | configuration, 200 |
| POST /api/application-configurations/:companyId/:id/delete | {} | {deleted:true} |
CONFIRMADO: no GET de detalle Resource por ID en registerResourceRoutes; la ficha resuelve un elemento del listado completo. No DELETE HTTP: la eliminación usa POST. Tampoco endpoint de búsqueda Resource por nombre, manufacturer/scope/active, paginación de recursos o candidatos de aplicaciones. El query environmentId no es búsqueda de catálogo. No modificar el significado de /resource-catalog/global: hoy contiene definiciones de campos/plantillas, no productos.
DTO y validación
ResourceInput: name, templateId, active, notes, characteristics e integration opcional. No companyId/scope en values. ResourceDTO: id, companyId UUID no nullable, specification, values, history y proyecciones opcionales. ResourceRevision exige valores, actorId, actorName y fecha, con specification opcional en transporte por compatibilidad; persistencia actual reconstruye especificación de cada revisión. input: resource-values.ts:40 dto: inventory.ts:35 mapper: resource-persistence.mapper.ts:24.
CatalogItemDTO sí tiene companyId nullable, builtin/builtinKey, active/effectiveActive/overridden, values y bindings. No reutilizarlo como DTO de Resource global: describe otra entidad. DTOs se infieren de Zod; varios common reexportan schemas de dominio, una organización existente distinta de las carpetas dto/request/response prescritas en normativa, sin refactor en esta auditoría. catalogDTO: index.ts:19 softwareDTO: software-installations.ts:19 applicationDTO: application-configurations.ts:20.
Navegador: inventoryApi usa csrfFetch y valida respuestas con InventoryResponseSchema/ResourceResponseSchema. Error usa {code,requestId} desde servidor; rutas traducen INVALID_REQUEST→400, sufijo NOT_FOUND→404 y otros fallos de negocio→409. Autenticación→401; el permiso global futuro necesita un tratamiento FORBIDDEN→403 explícito, no acabar en 409 por la función fail actual. inventoryAPI: inventory-api.ts:11 routes: resource.routes.ts:54.
Alternativas de API
| Alternativa | Ventajas | Costes / seguridad | Recomendación |
|---|---|---|---|
| Ampliar listado existente siempre | Menos llamadas | Rompe semántica local, licencias/remoto, tamaño, DTO nullable y nombres/enlaces | No como cambio implícito |
| Query scope en listado local | Descubrible y reutiliza route | Debe autorizar opciones y limitar usos; caché y defaults compatibles | Posible para vista futura, no suficiente para validación |
| Endpoint exclusivo catálogo global | Permisos/gestión claros, pagination propia | No resuelve unión ni referencias de compañía por sí solo | Sí para administración y lectura de productos |
| Endpoint candidatos por compañía/propósito | Datos mínimos, búsqueda y paginación coherentes, protege licencias | Nuevo contrato más resolución de usados | Preferido para selector |
| Dos APIs agregadas en navegador | Superficies separadas | Orden/paginación duplicados, dedup y errores parciales, fuga si ACL difiere | No como solución principal de búsqueda a volumen |
PROPUESTO, nombres finales por aprobar: gestión global en subruta propia del módulo Resources, separada de /resource-catalog; consulta de candidatos application/software ligada a companyId autorizado con search/cursor/pageSize; resolución por lotes de IDs para fichas/historial, incluidos inactivos. Los recursos locales conservan su CRUD y listado local.
La unión debe realizarse después de aplicar permisos y elegibilidad en servidor, antes de contar/paginar. Rama global disjunta de rama local; orden por coincidencia, nombre normalizado y UUID estable. Conservar procedencia en cada resultado; no deduplicar por nombre. Límite/cursor validados y consulta parametrizada. Repetir validación por ID al guardar aunque el selector ya lo ofreciera.
Contrato candidato mínimo propuesto: identidad, nombre, origen, active, clasificación y metadatos de producto necesarios (proveedor si existe). Excluir history completo, referencias Access y datos de instalaciones de otras compañías. No transmitir scope/companyId como sustituto de autorización. Consulta de usados y consulta de nuevos candidatos son contratos semánticos distintos: la primera necesita inactivos.
Compatibilidad y caché
| Cambio | Rotura derivable | Estrategia |
|---|---|---|
| companyId nullable | Zod, UniqueID, InventoryRow, builders, rutas de enlaces | Nuevo contrato discriminado de Resource y despliegue coordinado; legacy local sigue UUID |
| Añadir scope a strictObject | Un cliente con schema antiguo puede rechazar propiedad nueva | Versionar/adaptar lectores antes de emitir; no añadir silenciosamente a respuestas antiguas |
| Globales en listado company | Filtros de acceso/remoto y acciones de edición locales | Mantener listado antiguo local; candidatos mixtos explícitos |
| Procedencias múltiples | Etiquetas duplicadas, nombres no resueltos y clave insuficiente | ID único + origen visible + resolución de referencias |
| Global desactivado | desaparece de candidatos, sigue en historia | Resolver usados por ID e invalidar revisión, sin ocultar referencia |
Claves actuales: ['resource-inventory',companyId], catálogo por scope y operaciones por companyId. resourceRelatedQueryKeys(companyId) invalida algunas familias por prefijo amplio y otras por compañía. No modela cambios de producto global que afecten todas las compañías. inventoryAPI: inventory-api.ts:11 invalidation: resource-related-query-keys.ts:4.
PROPUESTO: claves diferenciadas de gestión global, candidatos (compañía, propósito, búsqueda, filtros, cursor), resolución por ID/lote y datos operativos. Cambio global invalida catálogos/candidatos/resoluciones y las queries operativas afectadas con estrategia explícita (p. ej. prefijo específico de software/aplicaciones cuando se recalcula needsReview); no toda la caché. Caché cliente no propaga invalidaciones a otros navegadores: definir refetch al recuperar foco o política de obsolescencia, y volver a validar siempre en escritura. No introducir caché global servidor sin clave de autorización/compañía.
Versiones mixtas: permitir DDL aditivo con escritores legacy solo mientras no existan globales visibles a lectores legacy; activation gate después de desplegar consumidores completos. El cambio del tipo no puede esconderse con casts ni llenar un companyId ficticio. Véase 03.