metis-desk/docs/auditorias/resources-global-scope/07-contratos-api.md
2026-10-25 01:19:17 +02:00

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.