navidev-abap-gw-api
v0.2.0
Published
API para la extracción de datos de servicios SAP GW
Readme
navidev-abap-gw-api
Cliente TypeScript para descubrir servicios SAP Gateway, leer metadata OData V2/V4 y ejecutar operaciones sobre sus entidades.
El proyecto utiliza una única SapConnection, compartiendo cookies, sesión y tokens CSRF con navidev-adt-api.
Desarrollo local
navidev-adt-api se instala desde npm como una dependencia normal. Instalación y validación:
npm install
npm run typecheck
npm run lint
npm testConexión
import {
GWCrudOperations,
GWMetadata,
GWServiceSearch,
SapConnection,
} from "navidev-abap-gw-api";
const connection = new SapConnection({
baseUrl: "https://sap.example",
client: "001",
language: "EN",
authentication: {
type: "basic",
username: "DEVELOPER",
password: process.env.SAP_PASSWORD!,
},
});
const metadata = new GWMetadata(connection);
const serviceSearch = new GWServiceSearch(connection);
const gateway = new GWCrudOperations(connection);Descubrir servicios
const result = await serviceSearch.searchService("ZUI");
if (result.isFailure) {
console.error(result.getErrorValue());
return;
}
console.log(result.getValue());La búsqueda devuelve servicios OData V2 y V4 normalizados.
Metadata
const result = await metadata.getMetadataInfo("ZUI_BOOKING_O2", {
ignoreValueHelp: false,
ignoreFields: false,
ignoreAnnotVocab: false,
});El resultado contiene namespace, entidades, campos, claves, restricciones y ayudas de búsqueda. Para V4 también indica si una entidad soporta draft mediante draftEnabled.
El binding puede consultarse de forma independiente:
const binding = await metadata.getBindingInfo("ZUI_BOOKING_O2");Abrir un servicio OData
GWCrudOperations es una factoría sin estado mutable. Cada llamada a openService() devuelve un cliente independiente ligado a un servicio concreto:
const serviceResult = await gateway.openService("ZUI_BOOKING_O2");
if (serviceResult.isFailure) {
console.error(serviceResult.getErrorValue());
return;
}
const service = serviceResult.getValue();
console.log(service.binding.versionOdata);
console.log(service.metadata.entities);Binding y metadata se cachean por nombre. Para forzar su recarga:
gateway.clearServiceCache("ZUI_BOOKING_O2");Es seguro abrir y usar varios servicios simultáneamente.
Consultas
El genérico representa un registro, no el array completo:
type Booking = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
};
const result = await service.query<Booking>("booking", {
select: ["TravelID", "BookingID", "BookingDate"],
filters: [
{ field: "TravelID", value1: "1" },
{
field: "BookingID",
option: FilterOptions.between,
value1: "1",
value2: "10",
},
],
sort: [{ field: "BookingID", descending: false }],
top: 20,
skip: 0,
count: true,
urlParameters: { "sap-client": "001" },
});
if (result.isSuccess) {
const bookings: Booking[] = result.getValue();
}También se admiten filterComplex, expand, search y skipToken.
Paginación
queryPage() conserva la información de paginación que devuelve SAP:
const pageResult = await service.queryPage<Booking>("booking", {
top: 20,
count: true,
});
if (pageResult.isSuccess) {
const page = pageResult.getValue();
console.log(page.value);
console.log(page.count);
console.log(page.nextLink);
}Leer por clave
const result = await service.read<Booking>("booking", {
TravelID: "1",
BookingID: "1",
});Crear, actualizar y borrar
const created = await service.post<Booking, Partial<Booking>>("booking", {
TravelID: "1",
BookingID: "1",
CustomerID: "100000",
});
const updated = await service.patch<Booking, Partial<Booking>>(
"booking",
{ TravelID: "1", BookingID: "1" },
{ CustomerID: "100001" },
);
const deleted = await service.delete("booking", {
TravelID: "1",
BookingID: "1",
});Draft OData V4
Las operaciones draft solo se aplican cuando la metadata identifica la entidad como draft-enabled.
Actualizar y activar:
const result = await service.post<Booking, Partial<Booking>>(
"Bookings",
{
TravelID: "1",
BookingID: "1",
CustomerID: "100001",
},
{ v4: { activateDraft: true } },
);Descartar un draft:
await service.post<Booking, Pick<Booking, "TravelID" | "BookingID">>(
"Bookings",
{ TravelID: "1", BookingID: "1" },
{ v4: { discardDraft: true } },
);Las acciones requieren todas las claves y un ETag válido. Los errores de consulta, preparación, activación o descarte se devuelven como Result.fail.
Petición dinámica
Para acciones, funciones o rutas no cubiertas por el CRUD:
const response = await service.request<{ value: string }>({
path: "booking/Example.Submit",
method: "POST",
query: { "sap-client": "001" },
body: { TravelID: "1", BookingID: "1" },
});
if (response.isSuccess) {
console.log(response.getValue().data);
console.log(response.getValue().status);
console.log(response.getValue().headers);
}Solo se aceptan rutas relativas al servicio. URLs absolutas, barras invertidas y segmentos .. son rechazados. Las escrituras reutilizan automáticamente la gestión CSRF de SapConnection.
Verificación
npm run typecheck
npm run lint
npm test
npm pack --dry-run