VCF Automation offers a polyglot programming environment, with JavaScript, PowerShell and Python as programming languages. We have the opportunity to expand this range to include additional programming languages. This post shows how the
TypeScript programming language can be seamlessly used.
Integrate TypeScript Programming Language
Here are the necessary actions required for this:
- testTypeScript: Contains the TypeScript source code.
- getActionAsText: JavaScript that reads the TypeScript source code.
- handler: JavaScript to call the transpilation with the TypeScript compiler.
- transpileTypeScript: JavaScript to transpile TypeScript code.
- invokeTypeScript: JavaScript to invoke TypeScript code.
- invokeMixScript: JavaScript to invoke TypeScript code with VCF Automation objects.
Invoke TypeScript
The following flowchart shows the steps for executing TypeScript code.
Invoke MixScript
The following flowchart shows the steps for executing TypeScript code using VCF Automation objects. By using the REST interface it is possible to build an action that can be executed like the standard. This means that all objects available in VCF Automation can also be used.
Save TypeScript Code as Action
In the first step we build an action, who we called testTypeScript, in which we store the TypeScript code to be executed. We simply save the TypeScript source code as an action, regardless if errors are displayed or not.

As we can see, this code doesn't do anything special. It is a standard example that is used in many internet sources. The addNumbers function adds only two numbers and the result is returned. The helloWorld function delivers a text with the input parameters. Not imaginative, but sufficient for our example.
Read Action as Text
In the second step we build a JavaScript action, called getActionAsText. As the name suggests, the content of an action is read out as text. With this action we read the TypeScript code and pass it for transpilation or execution.
Here all actions are filtered, first by the module name and then by the action name, to return the content of the specified action.
Transpile TypeScript to JavaScript
To provide the functionality of transpiling, we need to provide the TypeScript compiler with an interface in the third step.
The handler has two essentially blocks. The first defines the compiler options and the second do the transpilation. The block for checking the TypeScript code should only help to find errors that have occurred.

Now the handler with the TypeScript compiler and the necessary declaration files must be packed into a zip package. This package can now be loaded into VCF Automation and provided as an action.
Hint: To get the TypeScript compiler and the definition files,
download the latest release of TypeScript.
Hint: With the release 5.3.2 and highter of TypeScript is it better to add also the declaration files lib.dom.d.ts, lib.scripthost.d.ts and lib.webworker.importscripts.d.ts. Transpiling works perfectly without these files, but otherwise a diagnostic message occurs, that one of these files are missing.

To be able to easily use this transpilation, in conjunction with TypeScript code stored in an action, here an action that makes this possible.
Here an example of a transpilation. In the upper area the TypeScript source code and in the lower area the JavaScript code generated by the transpilation.
Invoke TypeScript
In fourth step the transpiled TypeScript code is executed with the Rhino Engine from VCF Automation. With this action we achieve a seamless embedding of TypeScript.
Hint: For this approach it is necessary to set the system property com.vmware.scripting.javascript.allow-native-object to true or add in the shutter file the org.mozilla.javascript.* package, or a superset of it.
Hint: The parameters are defined in a JSON string as a collection of key and value pairs. The values can be the primitive data types string, number, boolean and null.
Here is the example result of an execution.
Invoke TypeScript with VCF Automation Objects
The final and fifth step shows how TypeScript can also be used in conjunction with VCF Automation objects. The approach is very simple, the transpiled source code is saved as an action and this is then executed. Creation, execution and deletion of the action are done via the REST interface of VCF Automation.
Hint: The parameters are defined in a JSON object as an array of name, type and value. The values can be the primitive data types string, number, boolean and null.
var _actionActivities = function() {
this._url = null;
var fqdn = this.getFQDN();
if (fqdn !== null) {
this._url = "https://" + fqdn;
} else {
throw new Error("Error at FQDN detection");
}
this._httpRestHost = null;
if (this._url !== null) {
this._httpRestHost = RESTHostManager.createTransientHostFrom(
RESTHostManager.createHost("dynamicRequest")
);
this._httpRestHost.operationTimeout = 60;
this._httpRestHost.connectionTimeout = 30;
this._httpRestHost.hostVerification = false;
this._httpRestHost.url = this._url;
}
this.bearerToken = null;
};
_actionActivities.prototype = {
getFQDN : function() {
var fqdn = "";
var jvmOpts = java.lang.System.getenv("JVM_OPTS");
if (jvmOpts !== null) {
var options = jvmOpts.split(" ");
options.forEach( function(option) {
if (option.substring(0, 19) === "-Dvco.app.hostname=") {
fqdn = option.substring(19, option.length);
}
});
}
if (fqdn !== "") {
return fqdn;
} else {
return null;
}
},
retrieveBearerToken : function(username, password) {
if (this._url === null) {
return;
}
var httpRestHost = this._httpRestHost.clone();
var jsonLogin = {
"username": username,
"password": password
};
var login = JSON.stringify(jsonLogin);
var request = httpRestHost.createRequest(
"POST",
"/csp/gateway/am/api/login?access_token",
login
);
request.contentType = "application/json";
var response = request.execute();
if (response.statusCode === 200) {
var oRefreshToken = JSON.parse(response.contentAsString);
var refreshToken = "{\"refreshToken\":\"" +
oRefreshToken.refresh_token + "\"}";
request = httpRestHost.createRequest(
"POST",
"/iaas/api/login",
refreshToken
);
request.contentType = "application/json";
response = request.execute();
if (response.statusCode === 200) {
var oBearerToken = JSON.parse(response.contentAsString);
this.bearerToken = oBearerToken.token;
} else {
System.error("Error at retrieving bearer token");
}
}
},
createAction : function(
moduleName,
actionName,
code,
parameters,
outputType
) {
var _parameters = [];
if (parameters instanceof Array) {
_parameters = JSON.parse(JSON.stringify(parameters));
}
if (typeof outputType !== "string") {
outputType = "string";
}
var httpRestHost = this._httpRestHost.clone();
if (_parameters.length > 0) {
_parameters.forEach( function(parameter) {
delete parameter.value;
});
}
var jsonScript = {
"name": actionName,
"module": moduleName,
"version": "0.1.0",
"description": "Test",
"script": code,
"input-parameters": _parameters,
"output-type": outputType
};
var script = JSON.stringify(jsonScript);
var request = httpRestHost.createRequest(
"POST",
"/vco/api/actions?uniqueName=false",
script
);
request.contentType = "application/json";
request.setHeader("Accept", "application/json");
request.setHeader("Authorization", "Bearer " + this.bearerToken);
var response = request.execute();
if (response.statusCode === 201) {
var jsonResponse = JSON.parse(response.contentAsString);
return jsonResponse.id;
} else {
System.error("Error creating action");
throw new Error("Error creating action");
}
},
executeAction : function(actionId, parameters) {
if (!Array.isArray(parameters)) {
parameters = [];
}
var httpRestHost = this._httpRestHost.clone();
var jsonBody = {
"parameters": parameters,
"async-execution": false
};
var body = JSON.stringify(jsonBody);
var request = httpRestHost.createRequest(
"POST",
"/vco/api/actions/" + actionId + "/executions",
body
);
request.contentType = "application/json";
request.setHeader("Accept", "application/json");
request.setHeader("Authorization", "Bearer " + this.bearerToken);
var response = request.execute();
if (response.statusCode === 200) {
var jsonResponse = JSON.parse(response.contentAsString);
return jsonResponse;
} else {
System.log("Error at execution\n" + response.contentAsString);
}
},
getAction : function(actionId) {
var httpRestHost = this._httpRestHost.clone();
var request = httpRestHost.createRequest(
"GET",
"/vco/api/actions/" + actionId
);
request.setHeader("Accept", "application/json");
request.setHeader("Authorization", "Bearer " + this.bearerToken);
var response = request.execute();
return response.statusCode;
},
getActionLog : function(executionId) {
var httpRestHost = this._httpRestHost.clone();
var request = httpRestHost.createRequest(
"GET",
"/vco/api/actions/" + executionId + "/logs?maxResult=2147483647"
);
request.setHeader("Accept", "application/json");
request.setHeader("Authorization", "Bearer " + this.bearerToken);
var response = request.execute();
if (response.statusCode === 200) {
return response.contentAsString;
} else {
System.log("Error at get log");
}
},
deleteAction : function(actionId) {
var httpRestHost = this._httpRestHost.clone();
var request = httpRestHost.createRequest(
"DELETE",
"/vco/api/actions/" + actionId
);
request.setHeader("Authorization", "Bearer " + this.bearerToken);
var response = request.execute();
if (response.statusCode !== 200) {
System.log("Error at deletion");
}
}
};
function main(
userName,
password,
moduleName,
actionName,
parameters,
outputType
) {
if (
String(userName).trim() !== "" &&
String(password).trim() !== "" &&
String(moduleName).trim() !== "" &&
String(actionName).trim() !== ""
) {
var actionActivities = new _actionActivities();
actionActivities.retrieveBearerToken(userName, password);
if (actionActivities.bearerToken === null) {
throw new Error("No Bearer token available");
}
var code = System.getModule("de.stschnell").getActionAsText(
moduleName,
actionName
);
if (String(code).trim() === "") {
throw new Error("The action contains no code");
}
var transpiledCode = System.getModule("de.stschnell")
.transpileTypeScriptToJavaScript(code, actionName).javascriptSource;
var tempActionName = "x" + System.nextUUID().replace(/-/g, "_");
var actionId =
actionActivities.createAction(
"temp",
tempActionName,
transpiledCode,
parameters,
outputType
);
while (actionActivities.getAction(actionId) !== 200) {
System.sleep(1000);
}
var response = actionActivities.executeAction(
actionId,
parameters
);
var executionId = response["execution-id"];
actionActivities.deleteAction(actionId);
System.sleep(2500);
var actionLog = actionActivities.getActionLog(executionId);
return {
"returnType": response["type"],
"returnValue": JSON.stringify(response["value"]),
"executionId": executionId,
"actionLog": actionLog
};
} else {
throw new Error(
"userName, password, moduleName or actionName argument can not be null"
);
}
}
return main(
in_userName,
in_password,
in_moduleName,
in_actionName,
in_parameters,
in_outputType
);
|
Here is an example that uses the system object.
When transpiling from TypeScript to JavaScript, the use of the system objects is output as errors.
The following approach reads the single entries of the JSON object.
Conclusion
This approach shows that it is possible to integrate TypeScript directly into VCF Automation, and also save the source code. On this way we can use TypeScript seamlessly in VCF Automation. We can also use it to cover two development scenarios. On the one hand we can use the integrated transpiler to validate our TypeScript code and to transform it to JavaScript. On the other hand we can also execute the TypeScript code directly on different ways, and one of them with the full use of all VCF Automation objects.
Hint: The use of TypeScript will be discontinued, because ...
- the complexity of the development process increases.
- the advantages would only apply to a small proportion of JavaScript source codes in VCF Automation.
Addendum
Presentation at VMUG Community Event
On September the 10
th 2024 I presented the results of the TypeScript implementation at the VMware User Group (VMUG) Community Event in Kaiserlautern.
Open presentation