maximo-dev-tools
Version:
Tools used with the development of Maximo projects
1,426 lines (1,200 loc) • 55.4 kB
JavaScript
/* eslint-disable indent */
/* eslint-disable no-redeclare */
import axios from "axios";
import https from "https";
import { CookieJar, Cookie } from "tough-cookie";
import semver from "semver";
import {
InvalidApiKeyError,
LoginFailedError,
MaximoError,
MxAccessError,
MxAdminLogoutError,
MxDuplicateTransactionError,
PasswordExpiredError,
PasswordResetFailedError,
ResourceNotFoundError
} from "./errors.js";
import { fileURLToPath } from "url";
import * as fs from "fs";
import * as path from "path";
import MaximoConfig from "./maximo-config.js";
import { TextDecoder } from "util";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default class MaximoClient {
constructor(config) {
if (!(config instanceof MaximoConfig)) {
throw "config parameter must be an instance of MaximoConfig";
}
this.maxVersion = "undefined";
// keep a reference to the config for later use.
this.config = config;
this.requiredScriptVersion = "1.45.0";
this.currentScriptVersion = "1.45.0";
this.scriptEndpoint = "mxscript";
if (config.ca) {
https.globalAgent.options.ca = config.ca;
}
https.globalAgent.options.rejectUnauthorized = !config.allowUntrustedCerts;
// This is the way it is supposed to be done, but in tested Axios seems to ignore the agent.
// Allows untrusted certificates agent.
// let httpsAgent = new https.Agent({
// rejectUnauthorized: !config.allowUntrustedCerts,
// ca: config.ca
// });
this.jar = new CookieJar();
this.client = axios.create({
withCredentials: true,
// httpsAgent: httpsAgent,
baseURL: config.baseURL,
timeout: config.connectTimeout
});
this.client.interceptors.request.use(
function (request) {
// If the requested URL is the login endpoint, the inject the auth headers.
if (request.url === "login") {
this._addAuthHeaders(request);
if (this.config.apiKey) {
if (request.params) {
request.params["apikey"] = config.apiKey
} else {
request.params = { "apikey": config.apiKey };
}
}
if (request.params) {
request.params["csrf"] = "1";
} else {
request.params = { "csrf": "1" };
}
request.maxRedirects = 0;
request.validateStatus = function (status) {
return status == 200 || status == 302;
};
} else {
// // Add the x-public-uri header to ensure Maximo response URI's are properly addressed for external access.
// // https://www.ibm.com/docs/en/mema"s?topic=imam-downloading-work-orders-by-using-maximo-mxapiwodetail-api
request.headers["x-public-uri"] = this.config.baseURL;
if (this.config.apiKey) {
if (typeof request.params !== "undefined") {
let params = request.params;
params.lean = this.config.lean ? "true" : "false";
params.apikey = this.config.apiKey;
} else {
request.params = { "lean": this.config.lean ? "true" : "false", "apikey": this.config.apiKey };
}
} else {
if (typeof request.params !== "undefined") {
let params = request.params;
params.lean = this.config.lean ? "true" : "false";
request.params = params;
} else {
request.params = { "lean": this.config.lean ? "true" : "false" };
}
}
}
// @ts-ignore
this.jar.getCookiesSync(request.url && request.url.startsWith("http") ? request.url : request.baseURL, function (err, cookies) {
request.headers["cookie"] = cookies.join("; ");
});
return request;
}.bind(this)
);
this.client.interceptors.response.use(
function (response) {
const cookies = response.headers["set-cookie"];
if (cookies) {
let parsedCookies;
if (cookies instanceof Array) {
// @ts-ignore
parsedCookies = cookies.map(Cookie.parse);
} else {
parsedCookies = [Cookie.parse(cookies)];
}
parsedCookies.forEach((cookie) => {
this.jar.setCookieSync(cookie, response.request.protocol + "//" + response.request.host);
});
}
if (response.headers["csrftoken"]) {
this._csrfToken = response.headers["csrftoken"];
}
return response;
}.bind(this),
this._processError.bind(this)
);
// When the first created the state of the client is disconnected.
this._isConnected = false;
this._currentLogFile = undefined;
this._isLogging = false;
this._csrfToken = null;
}
get connected() {
return this._isConnected;
}
async connect() {
var response = await this.client.post("login");
var maxRedirects = 5;
var redirectUri = response.headers["location"];
if (response.status == 302 && this._isOIDCAuthRedirectResponse(response)) {
for (var i = 0; i < maxRedirects; i++) {
if (redirectUri == null) {
break;
}
response = await this.client.get(redirectUri, {
maxRedirects: 0,
withCredentials: true,
auth: { "username": this.config.username, "password": this.config.password },
validateStatus: function (status) {
return status == 200 || status == 302;
}
});
if (response.status == 302) {
// get the redirect URL from the header
redirectUri = response.headers["location"];
} else {
break;
}
}
} else if (response.status == 302 && this._isLTPAFormRedirect(response)) {
for (var i = 0; i < maxRedirects; i++) {
if (redirectUri == null) {
break;
}
if (redirectUri.includes("login.jsp?")) {
const headers = {
"content-type": "application/x-www-form-urlencoded"
};
const data = `j_username=${this.config.username}&j_password=${this.config.password}`;
response = await this.client.post(this.config.formLoginURL, data, {
maxRedirects: 0,
headers: headers,
withCredentials: true,
validateStatus: function (status) {
return status == 200 || status == 302;
}
});
await this.client.get(redirectUri);
response = await this.client.post("login");
break;
} else if (redirectUri.includes("loginerror.jsp")) {
this._isConnected = false;
throw new LoginFailedError("You cannot log in at this time. Contact the system administrator.");
} else {
response = await this.client.post(redirectUri, {
maxRedirects: 0,
withCredentials: true,
validateStatus: function (status) {
return status == 200 || status == 302;
}
});
if (response.status == 302) {
// get the redirect URL from the header
redirectUri = response.headers["location"];
} else {
break;
}
}
}
}
this._responseHandler(response);
}
_addAuthHeaders(request) {
request.headers.common["maxauth"] = this.config.maxauth;
if (!this.config.maxauthOnly) {
request.auth = { "username": this.config.username, "password": this.config.password };
}
request.withCredentials = true;
}
_isLTPAFormRedirect(response) {
if (!response) {
return false;
}
// Check whether this is a redirect response
if (response.statusCode < 300 || response.statusCode >= 400) return false;
const cookies = response.headers["set-cookie"];
if (cookies) {
var parsedCookies;
if (cookies instanceof Array) {
// @ts-ignore
parsedCookies = cookies.map(Cookie.parse);
} else {
parsedCookies = [Cookie.parse(cookies)];
}
if (!parsedCookies || parsedCookies.length == 0) {
return false;
}
// MAS8 sets matching cookies: WASOidcStateXXXXXX and WASReqURLOidcXXXXXX
// This is from specific observation and may need review/revision
var wasPostParamName = "WASPostParam";
var wasPostParamCookie = parsedCookies.filter((c) => c.key.toLowerCase().startsWith(wasPostParamName.toLowerCase()));
return wasPostParamCookie || wasPostParamCookie.length > 0;
} else {
return false;
}
}
_isOIDCAuthRedirectResponse(response) {
if (!response) {
return false;
}
// Check whether this is a redirect response
if (response.statusCode < 300 || response.statusCode >= 400) return false;
const cookies = response.headers["set-cookie"];
if (cookies) {
var parsedCookies;
if (cookies instanceof Array) {
// @ts-ignore
parsedCookies = cookies.map(Cookie.parse);
} else {
parsedCookies = [Cookie.parse(cookies)];
}
if (!parsedCookies || parsedCookies.length == 0) {
return false;
}
// MAS8 sets matching cookies: WASOidcStateXXXXXX and WASReqURLOidcXXXXXX
// This is from specific observation and may need review/revision
var oidcStateCookieNamePrefix = "WASOidcState";
var oidcStateCookie = parsedCookies.filter((c) => c.key.toLowerCase().startsWith(oidcStateCookieNamePrefix.toLowerCase()));
if (!oidcStateCookie || oidcStateCookie.length == 0) return false;
// determine the identifier for the corresponding req url cookie name.
var stateIdentifier = oidcStateCookie[0].key.substring(oidcStateCookieNamePrefix.length);
var oidcReqUrlCookieNamePrefix = "WASReqURLOidc";
var targetCookieName = oidcReqUrlCookieNamePrefix + stateIdentifier;
// ensure we have a matching req url cookie
return parsedCookies.filter((c) => c.key.toLowerCase() == targetCookieName.toLowerCase()).length > 0;
} else {
return false;
}
}
_responseHandler(response) {
if (response) {
if (response.status == 200) {
if (response.data && response.data.maxupg) {
this.maxVersion = response.data.maxupg;
}
this._isConnected = true;
} else if (response.status == 401) {
this._isConnected = false;
throw new LoginFailedError("You cannot log in at this time. Contact the system administrator.");
} else {
this._isConnected = false;
}
}
}
async disconnect() {
// we don't care about the response status because if it fails there is nothing we can do about it.
if (this._isConnected) {
try {
await this.client.post("logout", { withCredentials: true });
} catch (error) {
console.error("Warning disconnecting: " + JSON.stringify(error));
}
}
// can't be logging if disconnected.
this._isLogging = false;
}
async getScriptSource(script, fileName) {
if (!this._isConnected) {
await this.connect();
}
let isPython = fileName.endsWith(".py") || fileName.endsWith(".jy");
const options = {
url: "script/sharptree.autoscript.deploy/source/" + (isPython ? "/python" : ""),
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: script
};
await new Promise((resolve) => setTimeout(resolve, 100));
// @ts-ignore
const result = await this.client.request(options);
return result.data;
}
async dbConfigRequired() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/configdbrequired",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configDBRequired !== "undefined") {
return response.data.configDBRequired;
} else {
return false;
}
} else {
throw new MaximoError("Error checking if database configuration is required: " + response.data.error);
}
}
async dbConfigRequiresAdminMode() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/configdbrequiresadminmode",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configDBRequiresAdminMode !== "undefined") {
return response.data.configDBRequiresAdminMode;
} else {
return false;
}
} else {
throw new MaximoError("Error checking if database configuration requires admin mode: " + response.data.error);
}
}
async setAdminModeOn() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/adminmodeon",
method: MaximoClient.Method.POST,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configDBRequiresAdminMode !== "undefined") {
return response.data.configDBRequiresAdminMode;
} else {
return false;
}
} else {
throw new MaximoError("Error setting Admin Mode On: " + response.data.error);
}
}
async setAdminModeOff() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/adminmodeoff",
method: MaximoClient.Method.POST,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configDBRequiresAdminMode !== "undefined") {
return response.data.configDBRequiresAdminMode;
} else {
return false;
}
} else {
throw new MaximoError("Error setting Admin Mode Off: " + response.data.error);
}
}
async isAdminModeOn() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/adminmodeon",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.adminModeOn !== "undefined") {
return response.data.adminModeOn;
} else {
return false;
}
} else {
throw new MaximoError("Error checking if admin mode is on: " + response.data.error);
}
}
async dbConfigInProgress() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/configuring",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configuring !== "undefined") {
return response.data.configuring;
} else {
return false;
}
} else {
throw new MaximoError("Error checking database configuration is in progress: " + response.data.error);
}
}
async dbConfigMessages() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/configmessages",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
return response.data.messages;
} else {
throw new MaximoError("Error checking database configuration is in progress: " + response.data.error);
}
}
async applyDBConfig() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/sharptree.autoscript.admin/applyconfigdb",
method: MaximoClient.Method.POST,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.status !== "undefined" && response.data.status === "ok") {
if (typeof response.data.configDBRequiresAdminMode !== "undefined") {
return response.data.configDBRequiresAdminMode;
} else {
return false;
}
} else {
throw new MaximoError("Error applying database configuration: " + response.data.error);
}
}
async postConfig(json) {
if (!this._isConnected) {
await this.connect();
}
const configOptions = {
url: "script/sharptree.autoscript.deploy/config",
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: json
};
// @ts-ignore
await this.client.request(configOptions);
}
async postScript(script, fileName, deployScript) {
if (!this._isConnected) {
await this.connect();
}
let isPython = fileName.endsWith(".py") || fileName.endsWith(".jy");
if (deployScript) {
const deployOptions = {
url: "script/sharptree.autoscript.deploy" + (isPython ? "/python" : ""),
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: deployScript
};
// @ts-ignore
await this.client.request(deployOptions);
}
const options = {
url: "script/sharptree.autoscript.deploy" + (isPython ? "/python" : ""),
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: script
};
const result = await this.client.request(options);
if (result.data && result.data.status == "success" && typeof result.data.deployid !== "undefined") {
console.log(`Waiting for ${fileName} post deploy configuration to complete.`);
const checkOptions = {
url: "script/sharptree.autoscript.deploy",
method: MaximoClient.Method.GET,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
params: { "deployId": result.data.deployid },
data: script
};
// @ts-ignore
var checkResult = await this.client.request(checkOptions);
var checkCount = 0;
var progressMessages = [];
while (checkResult.data.deploying) {
checkCount++;
await new Promise((resolve) => setTimeout(resolve, 5000));
// @ts-ignore
checkResult = await this.client.request(checkOptions);
if (checkCount * 5000 > this.config.configurationTimeout) {
var minutes = this.config.configurationTimeout / 60000;
throw new MaximoError(
`The script deployed, but the configuration script exceed the time out of ${minutes} minute${
minutes > 1 ? "s" : ""
}. The configuration script may continue to execute in the background.`
);
}else{
if(typeof checkResult.data.progress !== 'undefined' && Array.isArray(checkResult.data.progress) && checkResult.data.progress.length > 0) {``
checkResult.data.progress.filter(
progress => !progressMessages.includes(progress.message)
).forEach(progress => {
progressMessages.push(progress.message);
console.log(progress.message);
});
}
}
}
checkResult.data.scriptName = result.data.scriptName;
return checkResult.data;
}else{
return result.data;
}
}
async postScreen(screen) {
if (!this._isConnected) {
await this.connect();
}
const options = {
url: "script/sharptree.autoscript.screens",
params: { designmode: false },
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: screen
};
const result = await this.client.request(options);
return result.data;
}
async postReport(report) {
if (!this._isConnected) {
await this.connect();
}
const options = {
url: "script/sharptree.autoscript.report",
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: report
};
const result = await this.client.request(options);
return result.data;
}
async postForm(form) {
if (!this._isConnected) {
await this.connect();
}
const options = {
url: "script/sharptree.autoscript.form",
method: MaximoClient.Method.POST,
headers: {
"Content-Type": "text/plain",
Accept: "application/json"
},
data: JSON.stringify(form, null, 4)
};
const result = await this.client.request(options);
return result.data;
}
async installed() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.where=autoscript="SHARPTREE.AUTOSCRIPT.DEPLOY"`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
try {
const response = await this.client.request(options);
if (!response || response.headers["content-type"] !== "application/json") {
throw new MaximoError("Received an unexpected response from the server. Content-Type header is not application/json.");
}
return response.data.member.length !== 0;
} catch (e) {
if (e.reasonCode && e.reasonCode === "BMXAA9301E") {
this.scriptEndpoint = "mxapiautoscript";
return await this.installed();
}
}
}
async upgradeRequired() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "script/SHARPTREE.AUTOSCRIPT.DEPLOY/version",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
if (typeof response.data.version !== "undefined") {
return semver.lt(response.data.version, this.requiredScriptVersion);
} else if (typeof response.data.status !== "undefined" && response.data.status === "error") {
throw new MaximoError(response.data.message);
} else {
return true;
}
}
async javaVersion() {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
var options = {
url: "",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
var response = await this.client.request(options);
if (response.data.thisserver) {
options = {
url: "members/thisserver/jvm",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
response = await this.client.request(options).catch((error) => {
// if the user doesn't have access to check the Java version then just skip it.
if (typeof error.reasonCode !== "undefined" && error.reasonCode === "BMXAA9051E") {
return "no-permission";
} else {
throw error;
}
});
// @ts-ignore
if (response === "no-permission") {
return response;
}
if (typeof response.data !== "undefined") {
return response.data.specVersion;
} else {
return "unavailable";
}
} else {
return "unavailable";
}
}
async maximoVersion() {
if (!this._isConnected) {
await this.connect();
}
if (typeof this.maxVersion !== "undefined" && this.maxVersion !== "unknown" && this.maxVersion !== "undefined") {
return this.maxVersion;
} else {
const headers = new Map();
headers["Content-Type"] = "application/json";
const options = {
url: "",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
const response = await this.client.request(options);
this.maxVersion = response.data.maxupg;
return this.maxVersion;
}
}
async deleteScriptIfExists(script) {
if (!this._isConnected) {
await this.connect();
}
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.where=autoscript="${script}"&lean=1`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
const result = await this.client.request(options);
if (result.data && result.data.member && result.data.member.length === 1 && result.data.member[0].href) {
let href = result.data.member[0].href;
let id = href.substring(href.lastIndexOf("/") + 1);
if (id) {
headers["x-method-override"] = "PATCH";
headers["patchtype"] = "MERGE";
if (this._csrfToken) {
headers["csrftoken"] = this._csrfToken;
}
options = {
url: `os/${this.scriptEndpoint}/` + id,
method: MaximoClient.Method.POST,
headers: { common: headers },
data: {
"_action": "Delete"
}
};
}
if ((await this.client.request(options)).status == "error") {
return false;
} else {
return true;
}
}
return false;
}
async installOrUpgrade(bootstrap) {
if (!this._isConnected) {
throw new MaximoError("Maximo client is not connected.");
}
if (bootstrap) {
var result = await this._bootstrap();
if (result.status === "error") {
return result;
}
await new Promise((resolve) => setTimeout(resolve, 500));
}
// eslint-disable-next-line no-undef
let source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.store.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.store", "Sharptree Automation Script Storage Script", source);
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.extract.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.extract", "Sharptree Automation Script Extract Script", source);
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.logging.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.logging", "Sharptree Automation Script Log Streaming", source);
// initialize the logging security.
result = this._initLogStreamSecurity();
if (result.status == "error") {
throw new MaximoError(result.message);
}
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.deploy.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.deploy", "Sharptree Automation Script Deploy Script", source);
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.screens.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.screens", "Sharptree Screens Script", source);
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.form.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.form", "Sharptree Forms Script", source);
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.library.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.library", "Sharptree Deployment Library Script", source);
// eslint-disable-next-line no-undef
source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.admin.js")).toString();
await this._installOrUpdateScript("sharptree.autoscript.admin", "Sharptree Admin Script", source);
}
// @ts-ignore
async startLogging(timeout) {
if (typeof timeout === "undefined") {
timeout = 30;
}
this._isLogging = true;
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `script/sharptree.autoscript.logging?timeout=${timeout}`,
method: MaximoClient.Method.GET,
responseType: "stream",
headers: { common: headers }
};
let lkp = undefined;
try {
while (this._isLogging) {
// @ts-ignore
if (typeof lkp !== "undefined") {
options.headers["log-lkp"] = lkp;
}
// @ts-ignore
let response = await this.client.request(options);
let contentType = response.headers["content-type"];
if (contentType === "application/json") {
if (typeof response.data !== "undefined") {
var internalError = await new Promise((resolve, reject) => {
let completeData = "";
response.data.on("data", (data) => {
if (!this._isLogging) {
resolve();
} else {
completeData += data;
}
});
response.data.on("end", () => {
if (completeData) {
try {
resolve(JSON.parse(completeData));
} catch (error) {
resolve();
}
} else {
resolve();
}
});
response.data.on("error", () => {
this.stopLogging();
reject();
});
});
if (internalError) {
throw new MaximoError(internalError.message);
} else {
throw new MaximoError("An unexpected JSON response was returned by the server.");
}
} else {
throw new MaximoError("An unexpected JSON response was returned by the server.");
}
} else if (contentType === "text/event-stream") {
lkp = await new Promise((resolve, reject) => {
let internalLKP = undefined;
response.data.on("data", (data) => {
if (!this._isLogging) {
resolve();
} else {
if (data && data instanceof Uint8Array) {
let decoder = new TextDecoder("utf-8");
let sData = decoder.decode(data);
if (sData.startsWith("log-lkp=")) {
internalLKP = sData.substring(8);
} else if (sData.indexOf("WARNING: Cannot set status. Response already committed.") > 0) {
// do nothing.
} else if (sData === "") {
// do nothing on a blank line
} else {
process.stdout.write(sData);
}
}
}
});
response.data.on("end", () => {
resolve(internalLKP);
});
response.data.on("error", () => {
this.stopLogging();
reject();
});
});
} else {
throw new Error(`Unexpected Content-Type ${contentType} was returned by the server.`);
}
}
} catch (error) {
if (error instanceof MaximoError) {
throw error.message;
}
var internalError = await new Promise((resolve, reject) => {
let completeData = "";
error.response.data.on("data", (data) => {
if (!this._isLogging) {
resolve();
} else {
completeData += data;
}
});
error.response.data.on("end", () => {
if (completeData) {
try {
resolve(JSON.parse(completeData));
} catch (error) {
resolve();
}
} else {
resolve();
}
});
error.response.data.on("error", () => {
this.stopLogging();
reject();
});
});
if (internalError) {
throw internalError;
} else {
throw error;
}
}
}
async stopLogging() {
this._isLogging = false;
this.disconnect();
}
async getAllScriptNames() {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.pageSize=10`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
var scriptNames = [];
let hasMorePages = true;
while (hasMorePages) {
// @ts-ignore
let response = await this.client.request(options);
if (response.data.member.length !== 0) {
response.data.member.forEach((member) => {
if (!member.autoscript.startsWith("SHARPTREE.AUTOSCRIPT")) {
scriptNames.push(member.autoscript.toLowerCase());
}
});
}
hasMorePages = typeof response.data.responseInfo.nextPage !== "undefined";
if (hasMorePages) {
let pageNumber = response.data.responseInfo.pagenum + 1;
options.url = `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.pageSize=10&pageno=${pageNumber}`;
}
}
return scriptNames;
}
async getAllScreenNames() {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: "script/sharptree.autoscript.screens",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data.screenNames;
} else {
throw new Error(response.data.message);
}
}
async getAllForms() {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: "script/sharptree.autoscript.form",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data.inspectionForms;
} else {
throw new Error(response.data.message);
}
}
async getAllReports() {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: "script/sharptree.autoscript.report",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data.reports;
} else {
throw new Error(response.data.message);
}
}
async getReport(reportId) {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `script/sharptree.autoscript.report/${reportId}`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data.report;
} else {
throw new Error(response.data.message);
}
}
// @ts-ignore
// eslint-disable-next-line no-unused-vars
async getPageData(url) {}
// @ts-ignore
// eslint-disable-next-line no-unused-vars
async extractScript(script) {}
async getScript(scriptName) {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `script/sharptree.autoscript.extract/${scriptName}`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data;
} else {
throw new Error(response.data.message);
}
}
async getScreen(screenName) {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `script/sharptree.autoscript.screens/${screenName}`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data;
} else {
throw new Error(response.data.message);
}
}
async getForm(formId) {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `script/sharptree.autoscript.form/${formId}`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.status === "success") {
return response.data.form;
} else {
throw new Error(response.data.message);
}
}
async _initLogStreamSecurity() {
let headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: "script/sharptree.autoscript.logging?initialize=true",
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
return response.data.status == true;
}
async _installOrUpdateScript(script, description, source) {
let scriptURI = await this._getScriptURI(script);
let headers = new Map();
headers["Content-Type"] = "application/json";
// update if a script uri was found.
if (scriptURI) {
let deployScript = {
"description": description,
"status": "Active",
"version": this.currentScriptVersion,
"source": source
};
headers["x-method-override"] = "PATCH";
if (this._csrfToken) {
headers["csrftoken"] = this._csrfToken;
}
let options = {
url: scriptURI,
method: MaximoClient.Method.POST,
headers: { common: headers },
data: deployScript
};
await this.client.request(options);
} else {
const deployScript = {
"autoscript": script,
"description": description,
"status": "Active",
"version": this.currentScriptVersion,
"scriptlanguage": "nashorn",
"source": source
};
const options = {
url: `os/${this.scriptEndpoint}`,
method: MaximoClient.Method.POST,
headers: { common: headers },
data: deployScript
};
await this.client.request(options);
}
}
async _getScriptURI(script) {
const headers = new Map();
headers["Content-Type"] = "application/json";
let options = {
url: `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.where=autoscript="${script}"`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
if (response.data.member.length !== 0) {
return response.data.member[0].href;
} else {
return null;
}
}
async _bootstrap() {
if (!this._isConnected) {
throw new MaximoError("Maximo client is not connected.");
}
let refUri;
try {
const headers = new Map();
headers["Content-Type"] = "application/json";
// eslint-disable-next-line no-undef
let source = fs.readFileSync(path.resolve(__dirname, "../resources/sharptree.autoscript.install.js")).toString();
let options = {
url: `os/${this.scriptEndpoint}?oslc.select=autoscript&oslc.where=autoscript="SHARPTREE.AUTOSCRIPT.INSTALL"`,
method: MaximoClient.Method.GET,
headers: { common: headers }
};
// @ts-ignore
let response = await this.client.request(options);
let href;
if (response.data.member.length === 1) {
href = response.data.member[0].href;
}
if (href) {
let deployScript = {
"description": "Sharptree AutoScript Deploy Bootstrap",
"status": "Active",
"version": this.currentScriptVersion,
"scriptlanguage": "nashorn",
"source": source
};
headers["x-method-override"] = "PATCH";
if (this._csrfToken) {
headers["csrftoken"] = this._csrfToken;
}
options = {
url: href,
method: MaximoClient.Method.PO