Files
basic-erp/apps/api/src/sl/sl-proxy.controller.ts
T
GeorgeandClaude Sonnet 5.5 c0990ca83e feat(api): proxy del Service Layer con registro de recursos
El frontend envía parámetros OData y cuerpo; el backend valida y ejecuta.

- GET lista/por clave, POST y PATCH sobre /api/sl/:recurso, solo para
  recursos registrados (ventas, compras, inventario y maestros estándar);
  EmployeesInfo no se registra nunca (contiene U_WEB_PASS).
- Permisos de menú por recurso (lectura/escritura); catálogos de consulta
  abiertos a cualquier sesión. Evaluador de patrones compartible
  (/inventario/* ya no habilita /inventario-x).
- Parámetros OData validados con lista cerrada, $top máx. 500, $expand solo
  si el recurso lo declara, claves validadas y escapadas por tipo, cabeceras
  B1S-* con lista blanca. $inlinecount y B1S-PageSize automáticos.
- configureApp() compartido entre main.ts y e2e (prefijo y límite de 5 mb).
- Verificado contra SAP real: los 27 recursos existen; listado con total,
  filtro, paginación y claves. InventoryRequests no existe en el SL: se
  registra InventoryTransferRequests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 19:04:52 -05:00

64 lines
2.0 KiB
TypeScript

import { Body, Controller, Get, Headers, HttpCode, Param, Patch, Post, Query, Req } from '@nestjs/common';
import type { AuthenticatedRequest } from '../auth/auth.types.js';
import { ZodValidationPipe } from '../common/zod-validation.pipe.js';
import {
type GetQuery,
type ListQuery,
bodySchema,
getQuerySchema,
listQuerySchema,
pickSlHeaders,
} from './sl-request.js';
import { SlProxyService } from './sl-proxy.service.js';
type Body = Record<string, unknown>;
type IncomingHeaders = Record<string, string | string[] | undefined>;
/** Proxy del Service Layer: `/api/sl/<Recurso>[/<clave>]` con parámetros OData del frontend. */
@Controller('sl')
export class SlProxyController {
constructor(private readonly proxy: SlProxyService) {}
@Get(':resource')
list(
@Req() req: AuthenticatedRequest,
@Param('resource') resource: string,
@Query(new ZodValidationPipe(listQuerySchema)) query: ListQuery,
@Headers() headers: IncomingHeaders,
) {
return this.proxy.list(req.user, resource, query, pickSlHeaders(headers, 'GET'));
}
@Get(':resource/:key')
get(
@Req() req: AuthenticatedRequest,
@Param('resource') resource: string,
@Param('key') key: string,
@Query(new ZodValidationPipe(getQuerySchema)) query: GetQuery,
) {
return this.proxy.get(req.user, resource, key, query);
}
@Post(':resource')
create(
@Req() req: AuthenticatedRequest,
@Param('resource') resource: string,
@Body(new ZodValidationPipe(bodySchema)) body: Body,
@Headers() headers: IncomingHeaders,
) {
return this.proxy.create(req.user, resource, body, pickSlHeaders(headers, 'POST'));
}
@Patch(':resource/:key')
@HttpCode(204)
async update(
@Req() req: AuthenticatedRequest,
@Param('resource') resource: string,
@Param('key') key: string,
@Body(new ZodValidationPipe(bodySchema)) body: Body,
@Headers() headers: IncomingHeaders,
): Promise<void> {
await this.proxy.update(req.user, resource, key, body, pickSlHeaders(headers, 'PATCH'));
}
}