69 lines
10 KiB
Markdown
69 lines
10 KiB
Markdown
# 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](../../../modules/resources/src/api/infrastructure/http/resource.routes.ts#L54) [catalogRoutes: resource-catalog.routes.ts:65](../../../modules/resources/src/api/infrastructure/http/resource-catalog.routes.ts#L65) [softwareHTTP: software-installation.routes.ts:116](../../../modules/companies/src/api/infrastructure/http/software-installation.routes.ts#L116) [applicationHTTP: application-configuration.routes.ts:116](../../../modules/companies/src/api/infrastructure/http/application-configuration.routes.ts#L116).
|
|
|
|
| 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](../../../modules/resources/src/api/domain/resource-values.ts#L40) [dto: inventory.ts:35](../../../modules/resources/src/common/inventory.ts#L35) [mapper: resource-persistence.mapper.ts:24](../../../modules/resources/src/api/infrastructure/persistence/resource-persistence.mapper.ts#L24).
|
|
|
|
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](../../../modules/resources/src/common/index.ts#L19) [softwareDTO: software-installations.ts:19](../../../modules/companies/src/common/software-installations.ts#L19) [applicationDTO: application-configurations.ts:20](../../../modules/companies/src/common/application-configurations.ts#L20).
|
|
|
|
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](../../../modules/resources/src/web/resources/shared/api/inventory-api.ts#L11) [routes: resource.routes.ts:54](../../../modules/resources/src/api/infrastructure/http/resource.routes.ts#L54).
|
|
|
|
## 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](../../../modules/resources/src/web/resources/shared/api/inventory-api.ts#L11) [invalidation: resource-related-query-keys.ts:4](../../../modules/resources/src/web/resources/shared/api/resource-related-query-keys.ts#L4).
|
|
|
|
**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](03-sequelize-mariadb.md).
|