remote-abap-compiler
Version:
Remote ABAP compilation
175 lines (173 loc) • 7.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const abap_adt_api_1 = require("abap-adt-api");
const randomstring_1 = __importDefault(require("randomstring"));
class CompilationRequest {
constructor(url, user, client, password, classPrefix = "RAC_", abapPackage = "$TMP", transportRequest) {
this.url = url;
this.user = user;
this.client = client;
this.password = password;
this.classPrefix = classPrefix;
this.abapPackage = abapPackage;
this.transportRequest = transportRequest;
this.adtClient = new abap_adt_api_1.ADTClient(url, user, password, client, "EN", { rejectUnauthorized: false });
this.className = classPrefix + randomstring_1.default.generate({
capitalization: "uppercase",
charset: "alphanumeric",
length: 30 - classPrefix.length,
});
this.classUrl = "/sap/bc/adt/oo/classes/" + this.className.toLowerCase();
if (this.transportRequest !== undefined && this.transportRequest === "") {
this.transportRequest = undefined;
}
}
compile(code) {
return __awaiter(this, void 0, void 0, function* () {
let mappedResult;
try {
yield this.createClass();
const result = yield this.putClassSource(this.getGlobalClassSource(), code);
mappedResult = this.mapToCompilationResult(result);
if (mappedResult.success) {
mappedResult.output = yield this.runClass();
}
}
finally {
yield this.cleanup();
}
return mappedResult;
});
}
cleanup(ignoreError = false) {
return __awaiter(this, void 0, void 0, function* () {
try {
this.adtClient.stateful = abap_adt_api_1.session_types.stateful;
const lock = yield this.adtClient.lock(this.classUrl, "MODIFY");
yield this.adtClient.deleteObject(this.classUrl, lock.LOCK_HANDLE, this.transportRequest);
yield this.adtClient.unLock(this.classUrl, lock.LOCK_HANDLE);
yield this.adtClient.dropSession();
}
catch (error) {
if (!ignoreError) {
throw error;
}
}
});
}
createClass() {
return __awaiter(this, void 0, void 0, function* () {
yield this.adtClient.statelessClone.createObject("CLAS/OC", this.className, this.abapPackage, "remote ABAP compiler", `/sap/bc/adt/packages/${this.abapPackage}`, undefined, this.transportRequest === "" ? undefined : this.transportRequest);
});
}
getGlobalClassSource() {
const source = `class ${this.className} definition public create public final.
public section.
interfaces if_oo_adt_classrun.
endclass.
class ${this.className} implementation.
method if_oo_adt_classrun~main.
new main( )->run( out ).
endmethod.
endclass.`;
return source;
}
putClassSource(globalClass, localTypes) {
return __awaiter(this, void 0, void 0, function* () {
this.adtClient.stateful = abap_adt_api_1.session_types.stateful;
const lock = yield this.adtClient.lock(this.classUrl, "MODIFY");
yield this.adtClient.setObjectSource(this.classUrl + "/source/main", globalClass, lock.LOCK_HANDLE, this.transportRequest);
yield this.adtClient.setObjectSource(this.classUrl + "/includes/implementations", localTypes, lock.LOCK_HANDLE, this.transportRequest);
yield this.adtClient.unLock(this.classUrl, lock.LOCK_HANDLE);
this.adtClient.stateful = abap_adt_api_1.session_types.stateless;
yield this.adtClient.dropSession();
let result = yield this.adtClient.activate(this.className, this.classUrl, undefined, true);
if (!result.success && result.inactive !== undefined && result.inactive.length > 0) {
const inactives = result.inactive.map((object) => object.object)
.filter((object) => object !== undefined);
if (inactives !== undefined) {
result = yield this.adtClient.activate(inactives, false);
}
}
return result;
});
}
mapToCompilationResult(activationResult) {
if (!activationResult.success && activationResult.messages.length > 0) {
return {
className: this.className,
errors: activationResult.messages.map(this.mapErrorMessages.bind(this)),
success: activationResult.success,
};
}
return {
className: this.className,
success: activationResult.success,
};
}
mapErrorMessages(message) {
const mainMethodErrorTag = `Class ${this.className}, Method IF_OO_ADT_CLASSRUN~MAIN`;
if (message.objDescr === mainMethodErrorTag) {
return this.mapErrorFromGlobalClass(message);
}
else {
return this.mapErrorFromLocalTypesInclude(message);
}
}
mapErrorFromLocalTypesInclude(message) {
const search = /#start=(\d+),(\d+)/g;
const match = search.exec(message.href);
if (match !== null) {
return {
errorMessage: message.shortText,
line: parseInt(match[1], 10),
offset: parseInt(match[2], 10),
};
}
else {
return {
errorMessage: message.shortText,
line: -1,
offset: -1,
};
}
}
mapErrorFromGlobalClass(message) {
if (message.shortText === "Type \"MAIN\" is unknown." ||
message.shortText === "Method \"RUN\" is unknown or PROTECTED or PRIVATE." ||
message.shortText.startsWith("The type \"MAIN\" is unknown") ||
message.shortText.startsWith("Method \"RUN\" does not exist")) {
return {
errorMessage: "Missing class named \"MAIN\" with public method \"RUN\".",
line: -1,
offset: -1,
};
}
else {
const defaultErrorMessage = "The method \"RUN\" of class \"MAIN\" " +
"must have exactly one importing parameter of type \"REF TO IF_OO_ADT_CLASSRUN_OUT\".";
return {
errorMessage: defaultErrorMessage,
line: -1,
offset: -1,
};
}
}
runClass() {
return __awaiter(this, void 0, void 0, function* () {
return this.adtClient.runClass(this.className);
});
}
}
exports.CompilationRequest = CompilationRequest;