Extending the Template Package
In order to ensure that Template Packages could specify as wide a range of Twinit applications as possible, and to allow developers to work beyond the current support of the manifest.json file, the template package specification has extensibility built in.
For instance, if you wanted to create a Template Package that would create Item Service collections during deployment, you;d have no way o do so currently, as the manifest specification does not support it. Or if you wish to create more complex permissions.
In order to ensure that developers could do those things and more, the template package and deployment gives you the capability for:
A complete example of a custom deployment is available at the end of this page.
Including Custom Files in the Template Package#
You can include any custom files in your template package in a custom folder. In the example below we have included a collections.json file in the template package.
Template Package.zip /||-- configs /|-- omapiConfig /|-- scripts /||-- custom /| |-- collections.json||-- manifest.json|-- README.mdYou can add as may files and folders as you like in the custom folder.
Why a 'custom' Folder?#
While the package file needs to be extensible, it also needs to expect future enhancements and improvements. In our collctions example, if you create a collections folder at the root and included your custom collections implementation in it, that folder and its contents could collide in the future with a manifest enhancement to handle collection deployment.
By using a custom folder, you ensure your custom implementations continue to function even when the manifest and deployment process change.
Including a Custom Initialization and Setup Script#
A custom initialization and setup script can also be included to run any deployment logic you would like, before and after the manifest based deploy has completed. Both are optional, and you need not use both or either.
- The initialization script will run immediately at the start of the template package deployment before any other deployment tasks. You can use it to perform any pre-deployment configuration, updates, migration, or clean up.
- The setup script runs last, after all other deployment tasks have finished. You can use it to complete any other setup needed, and is very useful for accomplishing tasks that the template package does not yet support or complex tasks like creating permissions.
You can configure an initialization and setup script by providing the paths to them in the manifest.
// manifest.json{ "Template Name": "Water Treatment Operations Digital Twin", "Template Version": "1.0.5", "initializeScript": "custom/initScript.mjs", "setupScript": "custom/setupMyTemplate.mjs"}Template Package.zip /||-- configs /|-- omapiConfig /|-- scripts /||-- custom /| |-- collections.json| |-- initScript.mjs| |-- setupMyTemplate.mjs||-- manifest.json|-- README.mdThe Initialization Script#
Your custom initialize script must be in a file with the extension .mjs and it must export a function named init.
// initScript.mjsexport async function init(input, libraries, ctx, callback) { // my initialization script content}The Setup Script#
Your custom setup script must be in a file with the extension .mjs and it must export a function named setup.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) { // my setup script content}The Script Parameters#
Both scripts are passed four parameters:
- input
- libraries
- ctx
- callback
The input Parameter#
The input parameter provides access to:
- your manifest
- the complete template package contents
- the project in which the deploy is happening
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input
// my setup script content}Using the manifest parameter you can access any configuration in manifest.json.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input
console.log(manifest['Template Version']) // 1.0.5 console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs
// my setup script content}Using packageData parameter you can access to the contents of the Template Package zip file and the contents of the files in it. The Template Package mechanism utilizes JSZip for access to the zip contents. You have full access to the JSZip API through the packageData object.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input
console.log(manifest['Template Version']) // 1.0.5 console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs
// contents of custom/collections.json as a string const collectionsFileContents = await packageData.file('custom/collections.json').async("string") // contents of custom/collections.json as parsed JSON const collectonsJSON = JSON.parse(collectionsFileContents)
// my setup script content}Using the project parameter you have access to the information for the project into which you are deploying.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input
console.log(manifest['Template Version']) // 1.0.5 console.log(manifest.custom.setupScript) // custom/setupMyTemplate.mjs
// contents of custom/collections.json as a string const collectionsFileContents = await packageData.file('custom/collections.json').async("string") // contents of custom/collections.json as parsed JSON const collectonsJSON = JSON.parse(collectionsFileContents)
console.log(project._name) // MyNewAppProject
// my setup script content}The libraries Parameter#
The libraries parameter provides access to the Twinit SDK JavaScript libraries.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { PlatformApi, IafScriptEngine } = libraries const { IafItemSvc, IafDataSource, IafPassSvc } = PlatformApi
// my setup script content}The ctx Parameter#
The ctx parameter is the user's current context who is running the deploy.
This is the ctx passed to the Twinit JavaScript SDK methods.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
const { PlatformApi, IafScriptEngine } = libraries const { IafPassSvc } = PlatformApi
const user = await IafPassSvc.getCurrentUser(ctx) console.log(user.firstname) // John console.log(user.lastname) // Doe
// my setup script content}The callback Parameter#
The callback parameter allows you send string messages back to the deployment process to be included in the final deployment logs.
Always check that callback exists before using.
// setupMyTemplate.mjsexport async function setup(input, libraries, ctx, callback) {
if (callback) callback('INFO: Beginning custom setup script execution!')
// my setup script content
if (callback) callback('INFO: Completed custom setup script execution!')}A Complete Example#
Template Package.zip /||-- configs /|-- omapiConfig /|-- scripts /||-- custom /| |-- collections.json| |-- initScript.mjs| |-- setupMyTemplate.mjs||-- manifest.json|-- README.md// manifest.json{ "Template Name": "Water Treatment Operations Digital Twin", "Template Version": "1.0.5", "initializeScript": "custom/initScript.mjs", "setupScript": "custom/setupMyTemplate.mjs"}// gets the current user from Twinit and sends a // message to the deployment resultsexport async function init(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input const { IafPassSvc } = libraries.PlatformApi
callback('INFO: Running initialize Script!')
const user = await IafPassSvc.getCurrentUser(ctx)
callback(`INFO: Current user is ${user._firstname} ${user._lastname}`)}
// setupMyTemplate.mjs// reads collections.json from the zip file and uses the contents// to create NamedUserCollections in the projectexport async function setup(input, libraries, ctx, callback) {
const { manifest, packageData, project } = input const { IafItemSvc } = libraries.PlatformApi
function sendCallback(message) { if (callback) callback(message) }
// example mesage callback to results sendCallback(`INFO: In script callback for project ${project._name}`)
// example of accesing data on the template manifest sendCallback(`INFO: script run from manifest for ${manifest['Template Name']} version ${manifest['Template Version']}`)
// example of using manifest data to access and read json data from the template package let collectionsString = await packageData.file('custom/collections.json').async("string") let collections = JSON.parse(collectionsString)
//example of using PlatformAPI to configure the project via this script if (collections && collections.length) { for (let coll of collections) { coll._namespaces = project._namespaces
try { let newCollections = await IafItemSvc.createNamedUserItems(collections, 'NamedUserCollection', ctx) sendCallback(`INFO: These collections created: ${newCollections._list.map(c => c._name).join(', ')}`) } catch(error) { sendCallback("ERROR: error creating collections in setup script, check console log for details") console.error("ERROR: error creating collections in setup script") console.error(error) }
} }
}