Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
navidev-adt-api
Advanced tools
El objetivo es disponer de una API que permita conectar con el ADT, Abap Developer Tools, para permitirnos crear programa, clases, mensajes, etc.. Desde cualquier aplicación hecha en JS/TS, ya que esta API ha sido creada en node para que pueda usarse en cualquier framework de JS, como React.
Esta herramienta o API se inspira, por no decir que algunas partes se basan en esa API, en la Abap ADT API creada por Marcello Urbani para su extensión de VS Code llamada ABAP Remote FS.
La diferencia entre ambas API , aparte que la mia tiene menos funcionalidad al menos incialmente, es que la he creado intentado usar metodología DDD, para tener una mejor estructuración. Y algunas cosas de que Marcello las hace de una manera yo las he planteado de otra manera.
Y me gustaría destacar el esfuerzo de Marcello para crear semejante API y extensión de VS Code.
Desde el terminal solo tenemos que instalarlo con nuestro gestor de paquetes favorito:
pnpm install navidev-adt-api
o
npm install navidev-adt-api
En nuestro código lo primero es importarla de la siguiente manera:
import AdtAPI from "navidev-adt-api/AdtAPI"
A destacar lo siguiente:
En el siguiente apartado se explica como recuperar los valores y errores
A modo de ejemplo pongo la llamada que nos devuelve una lista con todos los objetos del ADT:
Ejemplo de llamada:
adtApi.repositoryreadTypesStructure().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as RepositoryTypesStructure;
console.log(values);
} else if (response.isFailure) {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
La respuesta tiene dos variables: isSucces que será true si la petición ha ido correctamente, y recuperaremos los valores a través del método getValue, y la segunda isFailure que será true si se ha producción cualquier tipo de error del proceso, el error se recuperá mediante el metodo getErrorValue. El error devuelto será siempre del tipo AdtException. Habrá que mirar los campos informados que variarán según a la API que se llame.
Este ejemplo se hace un else if para mirar si ha fallado a modo de ejemplo completo, pero realmente si isSuccess es false es que se ha producido un error que habrá que procesarlo tal como se ve en el ejemplo.
Esta manera de recuperar los valores y errores será igual para toda la API.
Laa funcionalidad de la API esta repartida entre varias clases. En cualquier caso, siempre se instanciará pasandole los datos de conexión:
let adtConnection = new AdtConnection({
client: "001",
language: "EN",
password: "password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
En el ejemplo se usa la clase AdtConnection.
Una vez instanciada se podrá acceder a todos los método disponibles que se explican a continuación.
Clase: AdtConnection
Método: Login
Este método aunque no es necesario, ya que se establece la conexión automática, si que se puede usar para dos cosas:
Ejemplo de llamada:
let adtConnection = new AdtConnection({
client: "001",
language: "EN",
password: "password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
}).login().then((response) => {
if( response.isSuccess){
let values = response.getValue() as ResultConnection;
console.log(`Bearer token: ${values.connectionAuth.bearerToken}`)
console.log(`Cookies auth: ${values.connectionAuth.cookiesAuth}`)
}
else{
let adtException = response.getErrorValue() as AdtException;
// Tratamiento del error
}
});
En el ejemplo se muestra en consola los dos campos de autentificación que se pasarán a los siguientes métodos de las clases que se quiera usar. Ejemplo:
adtConnection.login().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ResultConnection;
let adt = new AdtTransport({
client: "001",
language: "EN",
password: "password",
bearerToken: values.connectionAuth.bearerToken,
cookiesAuth: values.connectionAuth.cookiesAuth,
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
}
});
Al método también se le pasa el usuario y password por si los token de autentificación ya no son validos, han caducado, para que se vuelve hacer el login.
Clase: AdtGeneral
Método: getUsers
Devuelve los usuarios del sistema. Devuelve el codigo de usuario y el nombre.
Ejemplo de llamada:
let general = new AdtGeneral({
client: "001",
language: "EN",
password: "password",
bearerToken: values.connectionAuth.bearerToken,
cookiesAuth: values.connectionAuth.cookiesAuth,
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
general.getUsers().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as Users;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtRepository
Método: readTypesStructure
Devuelve el listado de los tipos en el arbol de estructura en ADT. Son los nodos que podemos ver en la SE80 o Eclipse.
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository.readTypesStructure().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as RepositoryTypesStructure;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
// Tratmiento del error
}
});
Clase: AdtRepository
Método: repositoryReadObjectTypes
Devuelve el listado de los tipos de objetos de ADT: Programa, clases, Webdynpro, etc..
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository.readObjectTypes().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as RepositoryObjectTypes;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtRepository
Método: repositoryReadCheckRuns
Devuelve el listado de los tipos de objetos que se pueden validar o compilar: programas, clases, funciones, diccionario, etc.
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository.repositoryReadCheckRuns().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as RepositoryCheckRuns;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtRepository
Método: repositorySearchMetadata
Devuelve la combinación de tipos objetos que se pueden usar para hacer búsquedas de objetos. Ejemplo: En clases nos devolverá el tipo CLASS y los subtipos: OA(atributo de clase), OC(clase), OCE(evento), etc..
Los campos destacados que devuelve el método son:
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository.searchMetadata().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchMetadatas;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtRepository
Método: searchObjectSingleType
Realiza la busqueda individual de objetos, es decir, por un tipo y subtipo. Al método se le pasa el tipo, subtipo y el texto a buscar. El texto a buscar puede ser un patron: *zclmyclass** para que devuelva los posibles valores.
Para búsqueda rápidas, como verificar si existe una clase o devolver paquetes que tengan un patrón, es recomendable usar el método repositoryQuickSearch de la API ya que no es necesario saberse el subtipo en concreto.
Los campos que devuelve el método son:
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository
.repositoryIndividualSearchObjects(
"CLAS",
"OC",
"zcl_zsap_tools_core_dpc_ext"
)
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchObjects;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
En este ejemplo de llamada se le pasa el tipo "CLAS" y el subtipo "OC" (clase en si misma) y el nombre de la clase a búscar.
Clase: AdtRepository
Método: quickSearch
Muy parecido a la búsqueda individual de objetos pero aquí la diferencia es que solo se pasa el tipo de objeto y el texto o patrón del objeto. Este método es el más indicando para búsquedas estilo:"Dame todos los paquetes que comiencen por ZCA*".
Los campos que devuelve el método son:
Ejemplo de llamada:
let adtRepository = new AdtRepository({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtRepository.quickSearch("DEVC", "zca*").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchObjects;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtPackage
Método: readAllPackages
Devuelve el listado todos los paquetes de desarrollo del sistema
Ejemplo de llamada:
let adtPackage = new AdtPackage({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtApi.packageReadPackages().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as PackagesList;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtPackage
Método: packageReadContent
Devuelve el contenido de un paquete. El contenido devuelto es parecido a la estructura que vemos en el Eclipse: |- Nombre del paquete |-- categories -> Categorias de los objetos: Codigo fuente, diccionario, clases de mensaje, etc. |---- objectTypes -> Tipos de objeto clases, programas, dominios, etc. |----- objects -> Objetos del tipo: Nombre del objeto. De este nodo destacar el valor del campo objectUri, esta es la URL que se le pasará a otros servicio como el que obtiene estructuras, etc.. y el campo objectName que nos servirá en método como el de activar |-- Subpaquetes -> Objeto que contiene la misma estructura que la anterior de manera recursiva.
Ejemplo de llamada:
let adtPackage = new AdtPackage({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtPackage.readPackagesContent("ZSAP_TOOLS").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as PackageContent;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtPackage
Método: packageReadContentFlat
Se basa en el método packageReadContent pero en vez de devolver los paquetes en subniveles los devuelve en un formato plano:
|- Nivel |- Paquete superior o padre |- Nombre del paquete |-- categories -> Categorias de los objetos: Codigo fuente, diccionario, clases de mensaje, etc. |---- objectTypes -> Tipos de objeto clases, programas, dominios, etc. |----- objects -> Objetos del tipo: Nombre del objeto. De este nodo destacar el valor del campo objectUri, esta es la URL que se le pasará a otros servicio como el que obtiene estructuras, etc.. y el campo objectName que nos servirá en método como el de activar
Ejemplo de llamada:
let adtPackage = new AdtPackage({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtPackage.readPackageReadContentFlat("ZSAP_TOOLS").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as PackageContentsFlat;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtAbapObject
Método: objectReadStructure
Devuelve la estructura de un objeto: variables, constantes, métodos, interfaces y su posición. Es decir, lo que en el eclipse se ve en el outline. Dependiendo del tipo de objeto devuelve varias posiciones dividido por secciones. Ejemplo si tenemos esta declaración:
INTERFACES zif_spt_core_app .
Nos devolverá dos secciones: definitionIdentifier donde indica linea y columna donde comienza y finaliza el texto zif_spt_core_app. Y otro registro con el bloque definitionBlock donde indica linea y columna donde comienza y finaliza el texto INTERFACES zif_spt_core_app .
Al método se le pasa la URL del objeto que se quiere obtener los datos. Esa URL se recupera de la lectura de los objetos de los paquetes en el array objects campo objectUri.
La estructura que devuelve es:
|- Campos del objeto y tipo |-- structureElements Array con las variables, constantes, etc.. que se usa |--- blockInfo Array con la información de los bloques y su posiciones.
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject
.readObjectStructure("/sap/bc/adt/oo/classes/zcl_spt_apps_base")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectStructure;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtAbapObject
Método: checkRun
Hace la validación de sintaxis de un objeto. La validación de la sintaxis se puede hacer de dos maneras:
Ejemplo de como se haría la llamada en este caso:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject.checkRun("/sap/bc/adt/oo/classes/zcl_spt_apps_base")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectCheckRun;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject.checkRun("/sap/bc/adt/oo/classes/zcl_test_adt", [
{
sourceUri:
"includes/implementations",
artifactContent:
"KiIqIHVzZSB0aGlzIHNvdXJjZSBmaWxlIGZvciB0aGUgZGVmaW5pdGlvbiBhbmQgaW1wbGVtZW50YXRpb24gb2YNCioiKiBsb2NhbCBoZWxwZXIgY2xhc3NlcywgaW50ZXJmYWNlIGRlZmluaXRpb25zIGFuZCB0eXBlDQoqIiogZGVjbGFyYXRpb25zDQoNCmNsYXNzIGxjbF9jb250cm9sbGVyIERFRklOSVRJT04uDQpwdWJsaWMgc2VjdGlvbi4NCm1ldGhvZHMgbWV0b2RvMi4NCg0KRU5EQ0xBU1MuDQoNCmNsYXNzIGxjbF9jb250cm9sbGVyIElNUExFTUVOVEFUSU9OLg0KRU5EQ0xBU1Mu",
},
{
sourceUri:
"source/main",
artifactContent:
"Y2xhc3MgWkNMX1RFU1RfQURUIGRlZmluaXRpb24NCiAgcHVibGljDQogIGZpbmFsDQogIGNyZWF0ZSBwdWJsaWMgLg0KDQpwdWJsaWMgc2VjdGlvbi4NCg0KICBkYXRhIE1WX1ZBUjIgdHlwZSBTVFJJTkcgLg0KDQogIG1ldGhvZHMgTUVUT0RPMSAuDQogIFBST1RFQ1RFRCBTRUNUSU9OLg0KICBQUklWQVRFIFNFQ1RJT04uDQpFTkRDTEFTUy4NCg0KDQoNCkNMQVNTIFpDTF9URVNUX0FEVCBJTVBMRU1FTlRBVElPTi4NCg0KDQogIE1FVEhPRCBtZXRvZG8xLg0KICAgIFdSSVRFOi8gJ2RkJy4uDQogIEVORE1FVEhPRC4NCkVORENMQVNTLg==",
}
])
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectCheckRun;
console.log(values.messagesList);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Al ser un array se pueden pasar varios includes, tal como se ve en el ejemplo anterior. Eso si, en la versión de ABAP del trial versión que estoy usando si ambos includes tienen errores de código devuelve solo el primer error. Eso si, la API esta preparada para devolver todos los posibles mensajes que devuelta el ADT.
La URL principal del objeto viene cuando se leen los objetos de un paquete, en el array objects esta el campo objectUri con su valor.
El sourceUri de los artifacts depende del objeto que se esta procesando. En el caso de las clases se obtiene cuando se lee su contenido con el método classReadContent encontraremos el valor en el campo sourceUri en el array sourceIncludes.
En ambos tiposde llamada la estructura que devuelve es:
|- Campos de salida genericos de la validación. |-- messagesList Listado de con los mensajes devuelve en la validación. En el campo type con los valores: 'E' error y 'W' warning. shortText con el mensaje, pos posición del mensaje y quickFix si propuesta para arreglo rapido.
Clase: AdtAbapObject
Método: lock
Bloquea un objeto antes de poder editarlo. Si el objeto ya esta bloqueado se recupera quien lo esta bloqueando en la excepción que se recupera.
Al método se le pasa la URL que se recupera de la lectura de los objetos de los paquetes en el array objects campo objectUri.
En la respuesta del bloqueo hay que tener en cuenta dos campos para la edición:
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject.lock("/sap/bc/adt/oo/classes/zcl_spt_apps_base")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectLock;
console.log(`lock handle: ${values.lockHandle}`);
console.log(`Transport order: ${values.corrnr}`);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
});
Clase: AdtAbapObject
Método: unlock
Desbloquea un objeto. Al método se le pasa la URL que se recupera de la lectura de los objetos de los paquetes en el array objects campo objectUri, y se le pasa el valor del campo lockHandle que se recupera en el bloqueo.
El método no devuelve nada si todo ha ido bien.
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject
.lock("/sap/bc/adt/oo/classes/zcl_spt_apps_base")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectLock;
console.log(`lock handle: ${values.lockHandle}`);
console.log(`Transport order: ${values.corrnr}`);
adtObject
.unlock(
"/sap/bc/adt/oo/classes/zcl_spt_apps_base",
values.lockHandle
)
.then((response) => {
if (response.isSuccess) {
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
});
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
});
Clase: AdtAbapObject
Método: activate
Activa un objeto. Al método se le pasa la URL que se recupera de la lectura de los objetos de los paquetes en el array objects campo objectUri, y se le pasa el nombre del objeto, campo objectName que también estado en el array donde recuperamos el objectUri.
El método no devuelve nada si todo ha ido bien. Si hay errores de sintaxis en la activación los mensajes vienen en nodo messages.
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject
.activate("/sap/bc/adt/oo/classes/zcl_test_adt", "ZCL_TEST_ADT")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectActivate;
if (values.messages) console.log(values);
else console.log("ok");
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtAbapObject
Método: elementInfo
Devuelve la información de un elemento para una línea y posición del código fuente pasado. Al método se le pasa la url del objeto más su sección (para objetos donde tiene codigo fuente en varias secciones como las clases), se le pasa la línea y posición donde esta el elemento a obtener información y se le pasa el codigo fuente a analizar.
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtApi
.elementInfo(
"/sap/bc/adt/oo/classes/zcl_spt_apps_base/source/main",
32,
48,
content
)
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ObjectElementInfo;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtAbapObject
Método: readContent
Devuelve el contenido de una clase. Ese contenido se separa en dos partes:
Al método se le pasa la URL del objeto que se recupera al leer el contenido de los objetos de las clases de desarrollo.
Ejemplo de llamada:
let adtClass = new AdtClass({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtClass.readContent("/sap/bc/adt/oo/classes/zcl_spt_apps_base").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ClassContent;
console.log(values);
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException);
}
});
Clase: AdtAbapObject
Método: saveContent
Graba el contenido de una clase al método se le pasa:
Ejemplo de llamada:
let adtObject = new AdtAbapObject({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
let adtClass = new AdtClass({
client: "001",
language: "EN",
password: "Password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
});
adtObject
.lock("/sap/bc/adt/oo/classes/zcl_spt_apps_base")
.then((response) => {
if (response.isSuccess) {
adtApi
.classSave(
"/sap/bc/adt/oo/classes/zcl_test_adt",
"/source/main",
data,
values.lockHandle,
values.corrnr
)
.then((responseSave) => {
if (responseSave.isSuccess) {
console.log("clase grabada");
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
adtObject
.unlock(
"/sap/bc/adt/oo/classes/zcl_spt_apps_base",
values.lockHandle
)
.then((response) => {
if (response.isSuccess) {
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
});
})
} else {
let adtException = response.getErrorValue() as AdtException;
console.log(adtException.message);
}
});
La funcionalidad deque proporciona el ADT para la gestión de las ordenes de transporte dependerá de la versión del ABAP que se tenga. A mayor versión más funcionalidad se tiene.
Ejemplo, en la versión ABAP Trial Innovation Package que tiene la versión 7.51 es posible recuperar las ordenes de un usuario, añadir empleado, cambiar propietario o liberarlo. Mientras que en la versión ABAP Trial 2022 que contiene el ABAP 7.5H, la misma que en las ultimas versión del S/4 o ABAP Cloud, es posible crear ordenes, añadir objetos, borrarlos, etc..
Otro detalle importe es que hay información adicional, como en el método que recuperan las ordenes del usuario, que varia según la versión de ABAP. A mayor versión puede tener más información adicional.
Una manera que he detectado como distinguir las versiones antiguas de las más modernas es a través de la obtención de la configuración interna para obtener las ordenes del usuario, y será lo primero que se explique en los ejemplos. En los ejemplo se intentará comentar en que versión de ABAP esta disponible.
En los ejemplos que se pongan la parte de conexión se va omitir pero siempre se harán de la siguiente manera:
let adtConnection = new AdtConnection({
client: "001",
language: "EN",
password: "password",
username: "DEVELOPER",
urlBase: "http://vhcalnplci.dummy.nodomain:8000",
}).login().then((response) => {
if( response.isSuccess) {
let values = response.getValue() as ResultConnection;
let adt = new AdtTransport({
client: "001",
language: "EN",
password: "password",
bearerToken: values.connectionAuth.bearerToken,
cookiesAuth: values.connectionAuth.cookiesAuth,
username: "DEVELOPER",
urlBase: "http://vhcala4hci:50000",
});
}
else{
let adtException = response.getErrorValue() as AdtException;
// Tratamiento del error
}
} else {
let valuesError = response.getErrorValue();
console.log(valuesError);
}
});
Se podría llamar directamente a la clase AdtTransport sin necesidad de hacer login previamente, porque se hace login de manera automática.
Para haceros una idea la configuración es como la pantalla inicial de la transación SE09/SE10 que nos permite buscar las ordenes en el SAPGUI. Este servicio de configuración esta en versiones modernas de ABAP, en la 7.51 no existe.
Clase: AdtTransport
Método: getConfigurationMetadata
Obtiene los campos de selección que se usan para cambiar los valores de la configuración y devuelve el valor por defecto.
Este servicio en el ADT lo llama en la pantalla para cambiar los filtros de selección.
Ejemplo de llamada:
adt.getConfigurationMetadata().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportConfigurationMetadata;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: searchConfigurations
Es posible tener más de una configuración según como se llame al servicio de actualizar la configuración, pero he decido devolver solo la configuración más reciente tal como hace el ADT.
Nota: Este servicio solo esta disponible en versiones ABAP más recientes, con lo cual si el método te devuelve que no ha ido bien y el codigo de error 404 entonces estás en una versión de ABAP antigua y no podrás usar determina funcionalidad.
Ejemplo de llamada:
adt.searchConfigurations().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchConfigurations;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
if (valuesError.err==404)
console.log("Tienen una versión ABAP antigua")
else
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: getConfiguration
Al método hay que pasarle el ID de configuración obtenido con el método searchConfigurations y devuelve una estructura con los valores.
Ejemplo de llamada:
adt.searchConfigurations().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchConfigurations;
adt.getConfiguration(values.idConfiguration).then((response) => {
if (response.isSuccess) {
let valuesConf = response.getValue() as TransportConfiguration;
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: setConfiguration
Al método hay que pasarle el ID de configuración obtenido con el método searchConfigurations, la estructura de datos obtenida en el método getConfiguration pero con los ajustes necesarios y como opcional esta el valor ETAG que se recupera el método searchConfigurations. El campo ETAG si no se informa generará una configuración nueva y si se pasa se actualiza la existente. Vía ADT siempre se pasa por lo cual siempre se actualiza la misma.
Ejemplo de llamada:
adt.searchConfigurations().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as SearchConfigurations;
adt.getConfiguration(values.idConfiguration).then((response) => {
if (response.isSuccess) {
let valuesConf = response.getValue() as TransportConfiguration;
valuesConf.transportOfCopies = true;
valuesConf.dateFilter = TransportDateFilter.DateRange;
valuesConf.fromDate = new Date("2024-06-01");
valuesConf.toDate = new Date("2024-06-20");
adt
.setConfiguration(values.idConfiguration, valuesConf, values.etag)
.then((response2) => {
if (response2.isSuccess) {
let valuesConf2 =
response2.getValue() as TransportConfiguration;
console.log(valuesConf2);
} else {
let valuesError = response2.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Hay dos métodos para obtener las ordenes del usuarios según la versión de ABAP que se tenga. Lo he separado en dos métodos porque los parámetros de entrada son distintos.
En ambos métodos la estructura de datos que se devuelve es siempre la misma (como se ha comentado anteriormente habrá campos informados si la versión ABAP es más reciente) y es la siguiente:
|- workbench -> Array de ordenes de tipo Workbench |-- Task -> Array con las tareas |---- Objects --> Array con los objetos |---- Links --> Links con las acciones posibles |---- Campos informativos |-- Campos informativos |- customizing -> Array de ordenes de tipo customizing con los mismo subniveles que las ordenes de workbench.
Clase: AdtTransport
Método: getUsersOrdersWOConf
Se le pasa el usuario el cual queremos leer sus ordenes.
Ejemplo de llamada:
adt.getUsersOrdersWOConf("DEVELOPER").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as UserOrders;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
})
Clase: AdtTransport
Método: getUsersOrders
Tiene dos parámetros opcionales:
Ejemplo de llamada:
adt.getUsersOrders("DEVELOPER").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as UserOrders;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: detailOrderTask
Devuelve el detalle de una orden o tarea. La información devuelta es la misma que la que se recupera al obtener las ordenes del usuario, pero en este caso solo habrá una orden.
adt.detailOrderTask("A4HK900101").then((response) => {
if (response.isSuccess) {
//let values = response.getValue() as TransportAddObject;
//console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: addUserOrder
Versión ABAP: Todas
Método que se le pasa la orden y el usuario ha añadir. Este método es valido en todas las versiones ABAP.
Ejemplo de llamada:
adt.addUserOrder("A4HK900101", "BWDEVELOPER").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportTask;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
})
Clase: AdtTransport
Método: createOrder
Versión ABAP: > 7.51
Método que se le pasa la descripción de la orden, el tipo de la orden y el usuario de la misma.
Ejemplo de llamada:
adt.createOrder("prueba", TransportOrderTaskType.Workbench, "DEVELOPER")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportCreateOrder;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: deleteOrder
Versión ABAP: Todas
Método que se le pasa la orden o tarea a eliminar. Este método es valido en todas las versiones ABAP.
Ejemplo de llamada:
adt.deleteOrder("A4HK900044").then((response) => {
if (response.isSuccess) {
console.log("borrada");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
})
Clase: AdtTransport
Método: releaseOrder
Versión ABAP: Todas
Método que se le pasa la orden o tarea a liberar. Si la liberación no es posible, ejemplo se intenta liberar una orden sin haber liberado sus tareas o no pasar los controles del ATC, el mensaje de error se tiene que recuperar mediante el método getErrorValue de la clase devuelta en la API.
A modo informativo el ADT en el eclipse busca la el ID del ATC por defecto para pasarselo en el proceso de liberación. Este método esa búsqueda se hace por internamente, pero hay que tener en cuenta que siempre se ejecuta el ATC al liberar una orden. Igual que se lanza cuando se libera una orden desde la SE09/SE10.
Ejemplo de llamada:
adt.releaseOrder("A4HK900101").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportReleaseOrder;
console.log(values);
} else {
// Mensajes de error técnico y funcionales(validaciones ATC,
// otros validaciones propias de la liberación).
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: changeUserOrder
Versión ABAP: Todas
Método que se le pasa la orden o tarea y el nuevo usuario.
Ejemplo de llamada:
Ejemplo de llamada:
adt.changeUserOrder("NPLK900263", "BWDEVELOPER").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as ChangeUserOrder;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: changeTaskType
Versión ABAP: > 7.51
Cambia el tipo de tarea. Los tipos permitidos están en la enumeración TransportTaskType
Ejemplo de llamada:
adt
.changeTaskType("A4HK900100", TransportTaskType.DevelopmentCorrection)
.then((response) => {
if (response.isSuccess) {
console.log("tipo cambiado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: reassignTask
Versión ABAP: > 7.51
Método que se le pasa la tarea y la nueva orden.
Ejemplo de llamada:
adt.reassignTask("A4HK900100", "A4HK900098").then((response) => {
if (response.isSuccess) {
console.log("tarea reasignado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: sortAndCompress
Versión ABAP: > 7.51
Método que ordena y compacta los objetos de una orden/tarea
Ejemplo de llamada:
adt.sortAndCompress("A4HK900100").then((response) => {
if (response.isSuccess) {
console.log("realizado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: lockOrder
Versión ABAP: > 7.51
Bloquea una orden/tarea para poderla modificar.
Ejemplo de llamada:
adt.lockOrder("A4HK900101").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportLockObject;
console.log(values.lockHandle);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: unlockOrder
Versión ABAP: > 7.51
Desbloquea una orden/tarea pasandole el código de bloqueo obtenido en el método lockOrder
Ejemplo de llamada:
adt.lockOrder("A4HK900101").then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportLockObject;
console.log(values.lockHandle);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: moveObject
Versión ABAP: > 7.51
Metodo que permite mover el objeto de una orden/tarea a otra orden/tarea. Los datos del objeto se obtienen al recuperar las ordenes del usuario, por comodidad la estructura de entrada del método y la de los objetos en la orden/tarea es la misma.
Ejemplo de llamada:
adt
.moveObject(
{
name: "Z_PRUEBAS",
type: "PROG",
objInfo: "Program",
dummyUri:
"/sap/bc/adt/cts/transportrequests/reference?obj_name=Z_PRUEBAS&obj_wbtype=PROG&pgmid=R3TR",
wbtype: "PROG/P",
pgmid: "R3TR",
imgActivity: "",
lockStatus: "X",
objDesc: "pruebas de orden",
position: 1,
},
"A4HK900100", // Orden donde esta el objeto
"A4HK900102" // Orden donde se quiere mover
)
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportMoveObject;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: removeObject
Versión ABAP: > 7.51
Metodo que borra un objeto de una orden/tarea. Los datos del objeto se obtienen al recuperar las ordenes del usuario, por comodidad la estructura de entrada del método y la de los objetos en la orden/tarea es la misma.
Ejemplo de llamada:
adt
.removeObject(
{
name: "Z_PRUEBAS",
type: "PROG",
objInfo: "Program",
dummyUri:
"/sap/bc/adt/cts/transportrequests/reference?obj_name=Z_PRUEBAS&obj_wbtype=PROG&pgmid=R3TR",
wbtype: "PROG/P",
pgmid: "R3TR",
imgActivity: "",
lockStatus: "X",
objDesc: "pruebas de orden",
position: 1,
},
"A4HK900102"
)
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportRemoveObject;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: AddObject
Versión ABAP: > 7.51
Metodo que añade un objeto a una orden/tarea. El objeto que se añade se bloquea, en el caso que el tipo de objeto permita, de manera automática. Pero en la respuesta de ADT, y por consiguiente en la respuesta del método, no sale informado. Desconozo el motivo de esta incongruencia pero es recomemdable una vez añadido volver a leer los datos de la orden/tarea
Ejemplo de llamada:
adt
.AddObject("A4HK900102", "Z_PRUEBAS", "R3TR", "PROG")
.then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportAddObject;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Hay una serie de ayudas para búsqueda que se usan en distintos procesos de transportes, como la creación de ordenes, para poder montar las ayudas para búsqueda. Todos los métodos devuelven la misma estructura de datos.
Clase: AdtTransport
Método: getTargetVH
Versión ABAP: > 7.51
Devuelve los sistemas a los cuales se puede transportar una orden, como el sistema que se indica en transporte de copias. Se le puede pasar por parámetro un filtro de valores.
Ejemplo de llamada:
adt.getTargetVH().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportValueHelpItems;
console.log(values);console.log("tarea reasignado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: getCTSProjectVH
Versión ABAP: > 7.51
Devuelve los proyectos CTS. Se le puede pasar por parámetro un filtro de valores.
Ejemplo de llamada:
adt.getCTSProjectVH().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportValueHelpItems;
console.log(values);console.log("tarea reasignado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
Clase: AdtTransport
Método: getTypeVH
Versión ABAP: > 7.51
Devuelve los tipos de objeto. Se le puede pasar por parámetro un filtro de valores. Este método se usa para obtener el valor del campo "Program ID" a partir del tipo de objeto. Por ejemplo, si se buscr el tipo de objeto "PROG" en el campo data de la respuesta obtendremos el valor "PGMID = R3TR".
Ejemplo de llamada:
adt.getCTSProjectVH().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as TransportValueHelpItems;
console.log(values);console.log("tarea reasignado");
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError.message);
}
});
El ATC es el ABAP Test Cockpit.
Clase: AdtAtc
Método: getWorklist
El ID del ATC es un identificador que se pasa en algunos métodos, como al liberar una orden, para que ejecute las validaciones del ATC antes de realizar un proceso, como liberar una orden.
Ejemplo de llamada:
atc.getWorklist().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as AtcWorklist;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError);
}
});
Clase: AdtAtc
Método: getDefaultVariant
Devuelve el nombre de la variante por defecto del ATC
Ejemplo de llamada:
atc.getDefaultVariant().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as AtcDefaultVariant;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError);
}
});
Clase: AdtAtc
Método: getCustomizing
Devuelve la configuración del ATC.
Ejemplo de llamada:
atc.getCustomizing().then((response) => {
if (response.isSuccess) {
let values = response.getValue() as AtcCustomizing;
console.log(values);
} else {
let valuesError = response.getErrorValue() as AdtException;
console.log(valuesError);
}
});
FAQs
API de conexión al Abap Developer Tools
The npm package navidev-adt-api receives a total of 24 weekly downloads. As such, navidev-adt-api popularity was classified as not popular.
We found that navidev-adt-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.