navidev-abap-gw-api
v0.1.6
Published
API para la extracción de datos de servicios SAP GW
Readme
Objetivo
El objetivo es disponer de una API que permita obtener información de los servicios de gateway de SAP, con lo cual, pueda ser utilizado en otros proyectos fuera del mundo.
Que información se quiere sacar, pues de dos sitios: Metadata y Anotaciones. Del metadata sacaremos información básico de las entidades, campos y relaciones. Y de las anotaciones sacaremos más la info para la parte de UI en Fiori.
Para que quiero tener esta API. Pues para tener a futuro una herramienta que permite generar aplicaciones en React como si fueran Fiori Elements. Aunque no consiguiré que tengan la misma funcionalidad, pero poco a poco.
Instalación
Desde el terminal solo tenemos que instalarlo con nuestro gestor de paquetes favorito:
pnpm install navidev-abap-gw-apio
npm install navidev-gw-apiAPI
Busqueda de servicios
Clase: ServiceSearch
Método: searchService
Permite buscar los servicios por nombre, internamente se busca siempre por patron . Los servicios que se devuelven son los mismos que se ven en VS Code cuando se hace una nueva aplicación y seleccionamos un sistema de SAP, se devolverán tanto los servicios odata V2 como V4.
Los parámetros de entrada son:
- Nombre del servicio. Es un campo opcional e internamente se busca por patrón.
Ejemplo de llamada:
let serviceSearch = new ServiceSearch({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
serviceSearch.searchService("ZUI").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ServicesOData;
console.log(values);
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});MetadaInfo
Clase: Metadata
Método: getMetadataInfo
Obtiene la información de entidades y campos de un servicio. Es como llamar al servicio con $metadata al final. Pero a diferencia del metadata estándar, aquí se devuelve información como ayudas para búsqueda o anotaciones de fiori. Este método devuelve la información del servicio ya sea oData V2 o V4 normalizada en una misma estructura de datos, ya que la estructura del metadata estándar depende de la versión OData del servicio.
En el apartado de anotaciones no devuelve toda la información que tiene SAP para Fiori devuelve las básicas y se irán añadiendo a medida que se necesiten.
Los parámetros de entrada son:
- Nombre del servicio -> Puede ser de tipo Odata V2 o OData V4
- Opciones en la obtención de los datos:
- ignoreValueHelp -> No procesa las ayudas para búsqueda. Ya sean por anotación o info semántica (moneda/unidad de medida).
- ignoreFields -> No procesa los campos.
- ignoreAnnotVocab -> No procesa las anotaciones del vocabulario de Fiori.
Ejemplo de llamada:
let metadata = new Metadata({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
metadata.getMetadataInfo("ZUI_BOOKING_O2").then((response) => {
if (response.isSuccess) {
let values = response.getValue();
console.log(values);
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});Binding info
Clase: Metadata
Método: getBindingInfo
Obtiene la información de como llamar al servicio. Es decir, devuelve el path para poder llamar al servicio. Con ese path y la URL del sistema de SAP se construye la url para hacer las llamadas.
Ejemplo de llamada:
let metadata = new Metadata({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
metadata.getBindingInfo("ZUI_FIR_T003_O4").then((response) => {
if (response.isSuccess) {
let values = response.getValue();
console.log(values);
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});Operaciones CRUD
Las operaciones CRUD son los métodos para consultar, crear, actualizar y borrar datos. Las operación CRUD se realizan a través de la clase CrudOperations.
Con lo cual lo primero que hay que hacer es instanciar de la siguiente manera:
let crudOperations = new CrudOperations({
client: "001",
language: "EN",
password: "password",
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});A continuación hay que conectarse al servicio de la siguiente manera:
crudOperations.connect("ZUI_BOOKING_O2").then((response) => {
if (response.isSuccess) {
// METODO A EJECUTAR
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});Consultas
Métodos para la lectura de datos.
QUERY
El query devuelve un array de valores en base a los fitros introducidos.
query<types>(entidad,opciones)
En types se especifica un tipo de datos array con los campos que devolverá el servicio.
En entidad nombre de la entidad del servicio a la cual se quiere hacer la consulta.
En opciones son los parámetros de la consulta. Las opciones son las siguientes, todas los campos son opcionales:
- select -> Campos que se quieren recuperar de la consulta
- filters -> Filtros de la consulta. Se usará para hacer la llama de tipo query
- urlParameters -> Parámetros que se quieran pasar a la URL
- top -> Numero de registros a recuperar
- skip -> Junto al parámetro top se usa para hacer paginaciones
- sort -> Campos para realizar la ordenacióm
Ejemplo de una llamada query:
type Booking = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
};
type Bookings = Booking[];
crudOperations.connect("ZUI_BOOKING_O2").then((response) => {
if (response.isSuccess) {
crudOperations
.query<Bookings>("booking", {
filters: [
{ field: "TravelID", value1: "1" },
{ field: "TravelID", value1: "2" },
{
field: "BookingID",
value1: "1",
value2: "10",
option: FilterOptions.between,
},
],
top: 2,
})
.then((responseQuery) => {
if (responseQuery.isSuccess) {
let values = responseQuery.getValue();
console.log(values);
} else {
let valuesError = responseQuery.getErrorValue();
console.log(valuesError);
}
});
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});READ
El read devuelve un registro con el registro encontrado en base a la claves introducidas.
query<types>(entidad,campos clave)
En types se especifica un tipo de datos array con los campos que devolverá el servicio.
En entidad nombre de la entidad del servicio a la cual se quiere hacer la consulta.
En campos clave JSON con los campos clave del registro y sus valores.
Ejemplo de una llamada READ:
type Booking = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
};
crudOperations.connect("ZUI_DMO_BOOKING_O4").then((response) => {
if (response.isSuccess) {
crudOperations
.read<Booking>("booking", { TravelID: "1", BookingID: "1" })
.then((responseQuery) => {
if (responseQuery.isSuccess) {
let values = responseQuery.getValue();
console.log(values);
} else {
let valuesError = responseQuery.getErrorValue();
console.log(valuesError);
}
});
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});
Actualizaciones
Las actualizaciones son aquellos método que permitir añadir, actualizar o borrar registros
POST, oData V2
Método para añadir registros. A este método se le pasa la entidad, un JSON con los datos y los tipos de datos de los datos de actualización y respuesta.
Ejemplo de llamada:
type Booking = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
};
type BookingPost = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
FlightPrice: number;
CurrencyCode: string;
};
crudOperations.connect("ZUI_BOOKING_O2").then((response) => {
if (response.isFailure) {
let valuesError = response.getErrorValue();
console.log(valuesError);
return;
}
let booking: BookingPost = {
TravelID: "1",
BookingID: "1",
BookingDate: new Date(),
CustomerID: "1",
CarrierID: "1",
FlightPrice: 100.2,
CurrencyCode: "EUR",
};
crudOperations
.post<Booking, BookingPost>("booking", booking)
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
console.log(values);
});
});POST, oData V2
Con el OData V4 los datos no se graban directamente en la base de datos, sino, que primero se graban en la tabla de borrador. Luego hay que pasar de la tabla borrador a la tabla definitiva. Para simplificar estos pasos, se han puesto opciones en el método POST para simplificarlos que serán mostrados en los siguiente ejemplos.
Importante recalcar que debido al borrador sap necesita el parámetro oDataEtag que se usa para actualizar borrador o activar un borrador. Para simplificar las llamada este campo se determina internamente evitando tenerlo que pasarlo en cada momento.
En los ejemplo se usará este tipo de datos:
type BookingPost = {
TravelID: string
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
FlightPrice: number;
CurrencyCode: string;
};Cada llamado al método POST se engloba en esta llamada:
crudOperations.connect("ZUI_DB_BOOKING_O4").then((response) => {
if (response.isFailure) {
let valuesError = response.getErrorValue();
console.log(valuesError);
return;
}
// EJEMPLOS POST
});Inserción
La inserción más simple es la que se le hace un POST pasandole unos valores. Esta llamada simple siempre generárá un borrador. Tenga, o no, tenga registro existente.
let booking: BookingPost = {
TravelID: "1",
BookingID: "1",
BookingDate: new Date(),
CustomerID: "1",
CarrierID: "1",
FlightPrice: 20.22,
CurrencyCode: "EUR",
};
crudOperations
.post<Booking, BookingPost>("BOOKING", booking)
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
});Actualización
La actualización es muy parecida a la inserción:
let booking: BookingPost = {
TravelID: "1",
BookingID: "1",
FlightPrice: 24.22,
};
crudOperations
.post<Booking, BookingPost>("BOOKING", booking)
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
});En los parámetros ha actualizar siempre hay que pasarle los campos clave, más los valores a modificar. Si no se pasa los campos claves el servicio devolverá un error.
Activar borrador
En los dos pasos anteriores siempre se inserta y actualiza el borrador, pero si queremos que pase de manera automática de borrador al registro o definitivo. se haría de la siguiente manera:
let booking: BookingPost = {
TravelID: "1",
BookingID: "1",
BookingDate: new Date(),
CustomerID: "1",
CarrierID: "1",
FlightPrice: 20.22,
CurrencyCode: "EUR",
};
crudOperations
.post<Booking, BookingPost>("BOOKING", booking, {
v4: { activateDraft: true },
})
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
});Si se quiere activar un registro borrador pero no se quiere actualizar ningún campo se haría de la siguiente manera:
crudOperations
.post<Booking, BookingPost>("BOOKING", {}, {
v4: { activateDraft: true },
})
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
});PATCH, solo OData V2.
Es el método para actualizar registros. Este método a diferencia del PUT permite indicar solo los campos que se necesiten. Por lo que el método PUT no se implementa a favor de este.
A diferencia del método PATCH en Gateway, que solo devuelve si ha ido bien o no, este método devuelve el registro actualizado. Se hace un READ si no hay error en la actualización con la clave pasada por parámetro.
Este método solo se usa en el OData V2, para el V4 se usa el método POST para actualizar registros.
Ejemplo de llamada:
type Booking = {
TravelID: string;
BookingID: string;
BookingDate: Date;
CustomerID: string;
CarrierID: string;
};
type BookingPatch = Record<string, any>;
crudOperations.connect("ZUI_BOOKING_O2").then((response) => {
if (response.isFailure) {
let valuesError = response.getErrorValue();
console.log(valuesError);
return;
}
let booking: BookingPatch = {
FlightPrice: 300.2,
};
crudOperations
.patch<Booking, BookingPatch>(
"booking",
{ TravelID: "1", BookingID: "1" },
booking
)
.then((responsePost) => {
if (responsePost.isFailure) {
let valuesError = responsePost.getErrorValue();
console.log(valuesError);
return;
}
let values = responsePost.getValue();
console.log(values);
});
});