Plugin API examples
Setting a default attribute value
This script sets a default attribute value for a newly created information system.
'use strict';
// The function setDefaultValue is called each time a change event
// for an information system is received by the plugins
api.subscribeFor('InformationSystem', setDefaultValue);
function setDefaultValue(buildingblocktype, event) {
// More than one information system can be changed and
// the following code should be applied for each of them
for (var index = 0; index < event.length; index++) {
//We take one of the changes
var change = event[index];
// Default values should only be set when the element is newly created
// This is shown by the changeType
if (change.changeType == 'INSERT') {
// The plugin works with an updated model,
// hence the new building block is already contained in the model
// and has all values set that where set during the creation
var changedObject = api.datamodel.findByTypeAndId('InformationSystem', change.id);
// If no value is set, we add the default value.
// In case a value was set when creating the element no default value is needed
if (!changedObject.getValue("Status values")){
changedObject.setValue("Status values", "Current");
}
}
}
}
Enforcing upper limits for numbers
The following script enforces an upper limit for the attribute “costs” for projects. This is done by subscribing to the changes of projects and setting the value to 80 each time the attribute “costs” is set to greater than 80.
'use strict';
// We subscribe to the changes of projects
api.subscribeFor('Project', enforceUpperLimit);
// This function is called with a the currently used BBT and the changes that were made for this BBT
function enforceUpperLimit(buildingblocktype, event) {
// We loop over all changes for this building block type and save the current one in the variable "change"
for (var index = 0; index < event.length; index++) {
var change = event[index];
// We check if the element is created or updated, for deleted elements no values have to be adapted
if (change.changeType == 'INSERT' || change.changeType == 'UPDATE') {
// We get the BBT for which this change is
var changedObject = api.datamodel.findByTypeAndId(buildingblocktype, change.id);
// We loop over all changes to check if the costs value was changed
for (var i = 0; i < change.buildingBlockChanges.length; i++) {
if (change.buildingBlockChanges[i].persistentName == 'Costs') {
// If Costs are higher than 80: set to 80
if (changedObject.getValue("Costs") > 80){
changedObject.setValue("Costs", 80);
}
}
}
}
}
}
Validate status and date interval values (and send emails)
Schedule or directly execute a check: If the attribute “status” of an information system has the value “current”, the date interval attribute “productive period” must contain the current day. For each discrepancy, the accountable person for the element will be notified by email.
'use strict';
// The function validate is registered so it can be executed directly
// or by the scheduler
api.registerExecution(validate);
function validate() {
// We compare the end of the timespan with the current date,
// as this is the same for all instances we can create one variable
var currentDate = new Date().getTime();
// Returns all InformationSystems in the model
var allIS = api.datamodel.findByType('InformationSystem');
// We check all Informationsystems, one after the other
for (var i=0; i < allIS.length; i++) {
// We load the is and the attribute values necessary for the validation
var is = allIS[i];
var productivePeriod = is.getValue("Productive period");
var status = is.getValue("Status values");
// Ensure that all values are set, otherwise we cannot validate
// and calls would lead to errors
if (productivePeriod && productivePeriod.to && status) {
// The validation part, checks if an invalid state is there
if(status=="Current" && productivePeriod.to < currentDate){
// We want to send email to the accountable people,
// So we load the necessary data
var accountability = is.getValues("Accountability");
// More than one person could be accountable, we send the email to all of them
for (var index=0; index<accountability.length; index++){
var acc = accountability[index];
// In the attribute not only the loginName but also first and second name are saved
// We need to obtain the loginname itself
var loginName = acc.substring(acc.indexOf("(")+1, acc.length-1);
// Search for the user to get the email address
var user = api.user.get(loginName);
// Send an email only if the user has an email address
if(user.getEmail()){
var subject = "Invalid IS: "+ is.getValue("Name");
api.sendEmail(user.getEmail(), subject, "Please fix it");
}
}
}
} //if
} //for
}
Distributing costs
Each time the attribute “IT costs” of the building block type IT services is changed, the difference in value is transferred to the connected business units (a fraction depending on the number of related business mappings).
'use strict';
// This script is called each time an ItService changed
api.subscribeFor('ItService', onBuildingBlockChange);
function onBuildingBlockChange(buildingblocktype, event) {
// We loop over all changes to find the relevant ones
for (var index = 0; index < event.length; index++) {
// Take the next change
var change = event[index];
// This function is not called for deleted building blocks
if (change.changeType != 'DELETE') {
// The changed ItService and its connected BMs are taken from the model
var changedObject = api.datamodel.findByTypeAndId('ItService', change.id);
var bm = changedObject.getRelatedObjects('businessMappings');
// We initialize the variable for the cost differences
var costsDiff;
// We search for the change where the IT Costs changed
// and calculate the difference of these Costs
for (var i = 0; i < change.buildingBlockChanges.length; i++) {
if (change.buildingBlockChanges[i].persistentName == 'IT Costs') {
var added = change.buildingBlockChanges[i].added;
var removed = change.buildingBlockChanges[i].removed;
//api.printLog("change " + i + " added: " + added);
//api.printLog("change " + i + " removed: " + removed);
costsDiff = added - removed;
}
}
// Log Statments help to understand what the script does
// Especially when developing a script
api.printLog("costsDiff: " + costsDiff);
api.printLog("changedObject: " + changedObject.getId());
api.printLog("Business Mappings: " + bm.length);
// We only have to continue it the costs changed,
// Hence if costDiff is initialized (not undefined) and not 0
if (costsDiff) {
// In order to distribute related BMs are needed,
// otherwise nothing can be distributed
if (bm) {
for (var j = 0; j < bm.length; j++) {
// Within the BM we are interested in the connected business Unit
var bu = bm[j].getRelatedObject('businessUnit');
api.printLog("bu #" + j + ": " + bu.getId());
// We only continue if there is a related BU,
// otherwise there is nothing to obtain the costs
if (bu) {
// Load the costs of the related BU
var buCost = bu.getValue('IT Costs');
api.printLog("buCosts: " + buCost);
// We distribute the costs to the BUs
// by giving it the fraction of the Costs that applies
// when dividing equally for all BMs
if (!buCost) {
// If the BU does not yet have costs,
// the costs are set to the distributed value
var valueToSet = costsDiff / bm.length;
api.printLog("setting to " + valueToSet);
bu.setValue("IT Costs", valueToSet);
} else {
// If the BU has costs these costs are adapted
// corresponding to the distributed value
var valueToSet = buCost + (costsDiff / bm.length);
api.printLog("setting to " + valueToSet);
bu.setValue("IT Costs", valueToSet);
}
}
}
}
}
}
}
}
Releasing element seals
A new enum attribute called "seal" has to be created with the values "valid", "invalid" and "outdated" and a date attribute called "SealSetDate". Both attributes should be assigned to their own attribute groups. Then the permissions for the group can be set, so that only authorized users can change the seal and the date.
The different functions are within separate reactions to make it easier to read and maintain them, but they could also be placed within one reaction.
Each of the reactions has its own functionality:
The first one sets the seal to "Invalid" when an information system is changed and the seal is not updated.
The second one can be directly executed to outdate seals that are not changed within the last year.
The third one sets the attribute "SealSetDate" each time the seal is set to valid. This is necessary to have a date to check if a building block seal is outdated.
The last reaction is used to send e-mails. By default, it is commented out in the previous reactions.
Breaking the seal:
'use strict';
// The seal is only used for Information Systems hence only those have to be considered
api.subscribeFor('InformationSystem', onBuildingBlockChange);
// Each time an information System is changed we want to check if the seal breaks
function onBuildingBlockChange(buildingblocktype, event) {
// Loop over all changes to find the relevant ones
for (var index = 0; index < event.length; index++) {
// Each change is checked seperately
var change = event[index];
// The seal can only be broken when an element is updated
if (change.changeType == 'UPDATE') {
// We search for the changed Information System
var changedObject = api.datamodel.findByTypeAndId('InformationSystem', change.id);
// Check if there is a valid seal, otherwise nothing to do
if (changedObject.getValue("Seal") == "Valid") {
// We change the value if the seal itself was not changed
// Hence we initialize the boolean with true
var changeSeal = true;
// Check if the property seal was changed
// in this case the seal is not changed again
// otherwise it would become impossible to set the seals to valid
var buildingBlockChanges = change.buildingBlockChanges;
for (var i = 0; i < buildingBlockChanges.length; i++) {
if (buildingBlockChanges[i].persistentName == "Seal") {
changeSeal = false;
}
}
// If the seal becomes invalid because of the change it is invalidated here
if (changeSeal) {
changedObject.setValue("Seal", "Invalid");
// writeEmail(changedObject);
}
}
}
}
}
Reaction for outdated seals:
'use strict';
// The plugin is registered as Direct Execution Script
api.registerExecution(checkSealsIfOutdated);
function checkSealsIfOutdated() {
// We want to check all Information Systems
var allIS = api.datamodel.findByType('InformationSystem');
// We check each Information System seperately
for (var i=0; i < allIS.length; i++) {
// We get the Information System and its attribute value about when the seal was set
var is = allIS[i];
var sealDate = is.getValue("SealSetDate");
// Check if the valid seal was set more than one year ago
if (is.getValue("Seal") == "Valid" && sealDate && new Date() - sealDate >365*24*60*60000) {
// In this case the value of the Seal is changed to outdated
is.setValue("Seal", "Outdated");
// writeEmail(is);
}
}
}
Reaction for the date of the seal:
'use strict';
// The seal is only used for Information Systems hence only those have to be considered
api.subscribeFor('InformationSystem', setDateOfSeal);
function setDateOfSeal(buildingblocktype, event) {
// Loop over all changes to find the relevant ones
for (var index = 0; index < event.length; index++) {
//Each change is checked seperately
var change = event[index];
// When an element is deleted it does not exist anymore,
// and its values cannot be changed so this function is only called in the other cases
if (change.changeType == 'UPDATE' ||change.changeType == 'INSERT') {
// Getting all the changes for different attributes
var buildingBlockChanges = change.buildingBlockChanges;
// More than one value might have changed so we loop over all of them
// to find if there is a relevant change
for (var i = 0; i < buildingBlockChanges.length; i++) {
// We look for a change where the seal was set to valid
if (buildingBlockChanges[i].persistentName == "Seal" && buildingBlockChanges[i].added=="Valid") {
// Obtaining the changed information System
var changedObject = api.datamodel.findByTypeAndId('InformationSystem', change.id);
// Setting the SealSetDate to the current date,
// as this is the time when it was set to valid
var currentDate = api.createDate(new Date().getTime());
changedObject.setValue('SealSetDate', currentDate);
}
}
}
}
}
Reaction to send e-mails:
function writeEmail(changedObject) {
// We want to send email to the accountable people,
// So we load the necessary data
var accountability = changedObject.getValues("Accountability");
// More than one person could be accountable, we send the email to all of them
for (var index=0; index<accountability.length; index++){
var acc = accountability[index];
// In the attribute not only the loginName but also first and second name are saved
// We need to obtain the loginname itself
var loginName = acc.substring(acc.indexOf("(")+1, acc.length-1);
// Search for the user to get the email address
var user = api.user.get(loginName);
// Send an email only if the user has an email address
if(user.getEmail()){
var subject = "Not Valid Building Block: "+ changedObject.getValue("Name");
var content = "Dear user,\n\n"+
"Please note that the status of "+
changedObject.getValue("Name")+
" has changed and the data is no longer classified as valid. Please assess the actuality and correctness of the data and revalidate the status.\n\n"+
"Thank you very much,\n\n"+
"Max Mustermann\n"+
"Enterprise Architect"
api.sendEmail(user.getEmail(), subject, content);
}
}
}
Changing related elements
If an information system is changed, but not deleted, in our reactions "bob" is set as accountable for all related technical components. If a technical component is changed, we add all entries of its accountability to the related infrastructure elements.
Changes made by the plugin API do not trigger the plugin API again. By changing the information system, the technical component changes, but the infrastructure element does not.
Reaction for changing related elements:
'use strict';
// This code snipped contains two functions, which react to changes of different buildingblock types
api.subscribeFor('InformationSystem', onInformationSystemChange);
api.subscribeFor('TechnicalComponent', onTechnicalComponentsChange);
// When an Information System is changed, this function is called
function onInformationSystemChange(buildingblocktype, event) {
// We loop over all changes for this BBT and save the current one in a varaible "change"
for (var index = 0; index < event.length; index++) {
var change = event[index];
// We print to the log which Information System was created/updated/deleted
api.printLog(change.changeType + ' of InformationSystem with ID ' + change.id);
// We only continue if the element was not deleted
if (change.changeType != 'DELETE') {
// Get the element from the current datamodel
var changedObject = api.datamodel.findByTypeAndId('InformationSystem', change.id);
// Get all relation building blocks that connect the element with technical components
var relatedTechnicalComponentAssociations = changedObject.getRelatedObjects('technicalComponentAssociations');
// Each relation is connected to one technical component, add bob to the Accountability of this technical components
for (var i=0; i<relatedTechnicalComponentAssociations.length; i++) {
var technicalComponent = relatedTechnicalComponentAssociations[i].getRelatedObject('technicalComponent');
technicalComponent.addValue('Accountability','LUY Administrator (admin)');
}
}
}
}
// When a Technical Component is changed, this function is called
function onTechnicalComponentsChange(buildingblocktype, event) {
// Again we loop over all changes, and save the current one within an extra varaible.
for (var index = 0; index < event.length; index++) {
var change = event[index];
// We print information about change to the log
api.printLog(change.changeType + ' of TechnicalComponent with ID ' + change.id);
// We only continue if the element was not deleted
if (change.changeType != 'DELETE') {
// Again we search for the element and the related building blocks connecting this technical component with infrastructure elements
var changedObject = api.datamodel.findByTypeAndId('TechnicalComponent', change.id);
var relatedInfrastructureElementAssociations = changedObject.getRelatedObjects('infrastructureElementAssociations');
// Here we get the accountability values for the changed Object
var accountabilities = changedObject.getValues('Accountability');
// We loop over all related infrastructure elements and add all accountability values to the related element
for (var i = 0; i < relatedInfrastructureElementAssociations.length; i++) {
var infrastructureElement = relatedInfrastructureElementAssociations[i].getRelatedObject('infrastructureElement');
for (var j = 0; j < accountabilities.length; j++) {
infrastructureElement.addValue('Accountability', accountabilities[j]);
}
}
}
}
}
Traversing the element hierarchy
When a new parent is assigned to an information system, the value of the numeric attribute “Costs” is added to the attribute value for the superordinate information system. If the superordinate information system does not have a value for "Costs", it obtains the value of the child.
'use strict';
// We subscribe to the changes of information systems
api.subscribeFor('InformationSystem', onBuildingBlockChange);
// This function is called with a the currently used BBT and the changes that were made for this BBT
function onBuildingBlockChange(buildingblocktype, event) {
// We loop over all changes for this building block type and save th current one in the variable "change"
for (var index = 0; index < event.length; index++) {
var change = event[index];
// Here we only check changes for newly created and updated elements
if (change.changeType == 'INSERT' || change.changeType == 'UPDATE') {
var changedObject = api.datamodel.findByTypeAndId(buildingblocktype, change.id);
var buildingBlockChanges = change.buildingBlockChanges;
// We loop over all changes made for this building block
for (var i = 0; i < buildingBlockChanges.length; i++) {
//We are only interested in changes where the parent changed
if (buildingBlockChanges[i].persistentName == "parent") {
//We get the parent element
var parent = changedObject.getRelatedObject("parent");
//We only continue if a parent is set
if (parent) {
// Get the Costs of the parent and the changed object
var parentCost = parent.getValue("Costs");
var changedObjectCosts = changedObject.getValue("Costs");
//Only continue if the changed element has a cost value assigned
if (changedObjectCosts){
//if the parent has no Costs add the Costs of the current element
//otherwise add the costs of the current element to the costs of the parent
if (!parentCost) {
parent.setValue("Costs", changedObjectCosts);
} else {
parent.setValue("Costs", parentCost+changedObjectCosts);
}
}
}
}
}
}
}
}
Connecting and disconnecting business mappings and attributable relations
Connecting two building blocks via an attributable relation and disconnecting two elements connected via a business mapping when only the IDs of the building blocks are known.
Connecting business mappings and attribute relations:
'use strict';
// This function is registered on direct execution
api.registerExecution(onDirectExecute);
function onDirectExecute() {
// Get the information system and the technical component by id
var is = api.datamodel.findByTypeAndId('InformationSystem', 174);
var tc = api.datamodel.findByTypeAndId('TechnicalComponent', 311);
// Create a relation building block
var relation = api.datamodel.create('Is2TcAssociation');
// For a non-relation building block: set at least the name
// relation.setValue('name', 'Test');
// Add both elements to the relation object, this way they are connected to each other
relation.connect(is, "informationSystem");
relation.connect(tc, "technicalComponent");
}
Disconnecting business mappings and attribute relations:
'use strict';
// This function is registered on direct execution
api.registerExecution(onDirectExecute);
function onDirectExecute() {
// The ids of the two elements to be disconnected
var bfId = 24;
var boId = 51;
// We get all BMs related to the capabilities (interface is BusinessFunction calles ) so we do not have to check all BMs
var bf = api.datamodel.findByTypeAndId('BusinessFunction', bfId);
var allBMs = bf.getRelatedObjects('businessMappings');
// Print information to the log
api.printLog('The capability is related to '+ allBMs.length + ' business mappings');
// Loop over all BMs to find the one to delete
for (var index=0; index < allBMs.length; index++) {
if(allBMs[index].getRelatedObject('businessFunction') &&
allBMs[index].getRelatedObject('businessFunction').getId()== bfId &&
allBMs[index].getRelatedObject('businessObject') &&
allBMs[index].getRelatedObject('businessObject').getId()== boId &&
!allBMs[index].getRelatedObject('informationSystem')&&
!allBMs[index].getRelatedObject('businessUnit')&&
!allBMs[index].getRelatedObject('businessProcess')&&
!allBMs[index].getRelatedObject('product') &&
!allBMs[index].getRelatedObject('itService')){
// When the BM is found its id is printed to the log and the element is removed
api.printLog(allBMs[index].getId());
allBMs[index].remove();
}
}
}
List of all attributes and relations
With this reaction the names of all attributes and relations, for each building block type are printed to the reaction log.
'use strict';
//The function is registered for diret execution as it works independen of a change
api.registerExecution(getNames);
function getNames() {
//This array contains the names of all standalone building blocks
var allBBT = ['BusinessDomain', 'BusinessProcess','BusinessUnit','Product','Project','BusinessFunction', 'BusinessObject', 'InformationSystemDomain','InformationSystem', 'InformationFlow', 'ItService','ArchitecturalDomain','TechnicalComponent', 'InfrastructureElement']
// We loop over all BBTs to get the information for all of them
for (var index = 0; index < allBBT.length; index++) {
var buildingBlockType = allBBT[index];
// Log the current BBT
api.printLog( buildingBlockType );
// Print all attributes of the BBT
var attributedString = '=Attributes= ';
// Get all attributes for this BBT
var bbAttributes = api.datamodel.getAllPropertyNamesByType(buildingBlockType);
// Add them to the String
for (var attributeIndex = 0; attributeIndex < bbAttributes.length; attributeIndex++) {
attributedString += ' * ' + bbAttributes[attributeIndex];
}
//Log the created string
api.printLog(attributedString);
// Print all relations of the BBT
var relationString = '=Relations= ';
//Get all relations and add them to the string like for the attributes
var bbRelationTypes = api.datamodel.getAllRelationshipNamesByType(buildingBlockType);
for (var relationIndex = 0; relationIndex < bbRelationTypes.length; relationIndex++) {
relationString += ' * ' + bbRelationTypes[relationIndex];
}
//Print the String
api.printLog(relationString);
}
}
Getting information about a responsibility attribute
Print out the properties of the responsibility attribute “Accountability” for the project building block type in the logs.
'use strict';
api.registerExecution(onDirectExecute);
function onDirectExecute() {
//Get the information about the attribute "Accountability" of the type "Project"
var propertyInfoObject = api.getPropertyInfo("Project", "Accountability");
//Create the string to be logged
var propertyInfo = 'Project - Accountability '
+ '(type: ' + propertyInfoObject.type
+ ', multiple: ' + propertyInfoObject.multiple
+ ', mandatory: ' + propertyInfoObject.mandatory + ')';
// log the information
api.printLog(propertyInfo);
}
Logging which user made a change
This reaction subscribes to changes of information systems and prints out information regarding the last modification time and the user responsible for it in the logs.
'use strict';
// We subscribe for changes on Information Systems
api.subscribeFor('InformationSystem', onBuildingBlockChange);
// This function is called for each change on a information system and prints information about the user that changed it
function onBuildingBlockChange(buildingblocktype, event) {
// We loop over all changes and save them within a seperate variable
for (var index = 0; index < event.length; index++) {
var change = event[index];
// This function is called only for updated BBs
if (change.changeType == 'UPDATE') {
// We search for the BB
var changedObject = api.datamodel.findByTypeAndId('InformationSystem', change.id);
// Getting detailed user about the user that changed to object
var userString = changedObject.getValue('lastModificationUser');
var loginName = userString .substring(userString .indexOf("(")+1, userString .length-1);
var lastModificationUser = api.user.get(loginName);
// We print a string with all necessary information to the log
var lastModificationInfo = 'Information system \"' + changedObject.getValue('name') + '\" was modified by '
+ lastModificationUser.firstName + ' ' + lastModificationUser.lastName + '. Contact this person via email for any details: ' + lastModificationUser.getEmail();
api.printLog(lastModificationInfo);
}
}
}
Listing out previous values of enums and responsibility attributes
Each time the responsibility attribute “Accountability” is changed, a list of the previously accountable persons for the information systems or architectural domains is printed out in the logs.
By adapting this reaction, it is possible to obtain a list of the values before the change for any multivalue enums or responsibility attributes.
'use strict';
// The script is used for Information Systems and Architectural Domains, we react on changes of them
api.subscribeFor('InformationSystem', onBuildingBlockChange);
api.subscribeFor('ArchitecturalDomain', onBuildingBlockChange);
// Each time an information system changed we check if it obtained a new parent
function onBuildingBlockChange(buildingblocktype, event) {
// We loop over all changes
for (var index = 0; index < event.length; index++) {
var change = event[index];
// And consider only changes of updated information systems
if (change.changeType == 'UPDATE') {
// By using the type that is given as parameter the same script can be used for more than one type
var changedObject = api.datamodel.findByTypeAndId(buildingblocktype, change.id);
var buildingBlockChanges = change.buildingBlockChanges;
for (var i = 0; i < buildingBlockChanges.length; i++) {
// We check if the attribute accountability was changed, only for those changes we want to do something
if (buildingBlockChanges[i].persistentName == "Accountability") {
var changesOfAccountability = buildingBlockChanges[i];
// We start with the current set
var setBeforeChange = changedObject.getValues("Accountability");
// Then we add the values that were removed with the change
if (changesOfAccountability.removed) {
setBeforeChange = setBeforeChange.concat(changesOfAccountability.removed);
}
// Finally we only keep the values that are not added by the change
if (changesOfAccountability.added) {
setBeforeChange = setBeforeChange.filter(
function (item) {
return changesOfAccountability.added.indexOf(item) == -1;
}
)
}
// Now we can print the set like it was before the change
api.printLog("The set before the changes: " + setBeforeChange);
}
}
}
}
}
Execute an LDAP query
Executes an LDAP query and add users to a responsibility attribute.
'use strict';
api.registerExecution(onDirectExecute);
function onDirectExecute() {
var ldapResult = [];
// Executes an LDAP query to obtain information needed to create users
try {
ldapResult = api.executeLdapQuery("OU=Engineering", "(&(objectCategory=user)(objectClass=user))", ["sAMAccountName", "givenName", "sn", "mail"]);
} catch(e) {
api.printLog("LDAP error: " + e.message);
}
// Get the responsibility attribute where the users should be added
var attr = api.metamodel.getAttribute("responsibilityAttribute");
// Loop over all the information the LDAP execution returned
for (var i = 0; i<ldapResult.length; i++) {
var str = "";
// Save the information from LDAP in different constants
var loginName = ldapResult[i].sAMAccountName;
var firstName = ldapResult[i].givenName;
var lastName = ldapResult[i].sn;
var mail = ldapResult[i].mail;
// Create the user within the attribute
try {
// Check if data are null and use an empty string in this case
api.printLog("Create user: " + loginName);
firstName = firstName === null ? "" : firstName;
lastName = lastName === null ? "" : lastName;
mail = mail === null ? "" : mail;
// Create the user
var user = api.user.create(loginName, firstName, lastName);
user.setEmail(mail);
// Add the responsibility literal to the attribute
attr.createResponsibilityLiteral(user.getId(), "c0c883");
} catch(userEx) {
api.printLog("User create error: " + userEx.message);
}
}
}
This reaction only works if LUY is installed with the authentication method LDAP or LDAP+SSO
Execute an HTTP request
This reaction uses a public API to obtain information about the sun rise and sunset and prints it out in the logs. It shows how to use the HTTP functions of the plugin API.
'use strict';
api.registerExecution(httpSunset);
// Writes the time of the sunset at the current day to the log
function httpSunset(){
// Setting parameters for the http request
// This corresponds to GET on "https://api.sunrise-sunset.org/json?lat=48.13743&lng=11.57549"
api.http.setURL("https://api.sunrise-sunset.org/json");
api.http.setMethod("GET");
api.http.addParameter("lat", 48.13743)
api.http.addParameter("lng", 11.57549)
// Performing the request, returns a string
var result = api.http.execute();
// In order to work with the result, it is parsed to a JSON Object
//We can get more information from the response if needed
// api.printLog("The complete response body: " + result.getBody());
// api.printLog("All headers: " + JSON.stringify(result.getHeaders()));
// api.printLog("Server header: " + result.getHeaders().Server);
// api.printLog("Status Code: " + result.getStatus());
// In order to work with the result, it is parsed to a JSON Object
var resultJson = JSON.parse(result.getBody());
// We log the time of the sunset
api.printLog("Sunset in Munich today: " + resultJson.results.sunset);
}
Export a list of building blocks via HTTP
Filter a list of building blocks and then export selected fields as CSV to an HTTP endpoint.
"use strict";
api.registerExecution(exportList);
function exportList() {
var filteredIS = getFilteredList();
var csv = generateCsv(filteredIS);
postToEndpoint("https://example.endpoint", csv);
}
function getFilteredList() {
// first, retrieve all information systems
var allIS = api.datamodel.findByType("InformationSystem");
var filteredIS = [];
// in this example, we are only interested in IS where the attribute "Complexity" is "High"
for (var i = 0; i < allIS.length; i++) {
var complexity = allIS[i].getValue("Complexity");
if (complexity == "High") filteredIS.push(allIS[i]);
}
return filteredIS;
}
function generateCsv(list) {
var rows = [];
// now we generate one row for each information system in the list
// we want it to include the name, complexity and all connected business units
for (var i = 0; i < list.length; i++) {
var name = list[i].getValue("Name");
var complexity = list[i].getValue("Complexity");
var bms = list[i].getRelatedObjects("BusinessMappings");
var businessUnits = "";
// business mappings need to be handeled a bit differently
// we want to make sure each business unit is only contained once
bms.forEach(function(bm) {
var bu = bm.getRelatedObject("businessUnit");
if (bu) {
var buName = bu.getValue("Name");
if (businessUnits.indexOf(buName) == -1) {
if (businessUnits.length > 0)
businessUnits = businessUnits.concat("; ");
businessUnits = businessUnits.concat(bu.getValue("Name"));
}
}
});
rows[i] = [name, complexity, businessUnits];
}
// now we generate the actual csv
var csvContent = "";
rows.forEach(function(rowArray) {
var row = rowArray.join(",");
csvContent += row + "\r\n";
});
return csvContent;
}
// this method is used to post the csv content to some endpoint via http
function postToEndpoint(url, csv) {
api.http.setURL(url);
api.http.setMethod("POST");
api.http.setBody(csv);
api.http.execute();
}
Check responsible department for a building block
Check the responsible department of a building block and reject changes if the editing user is not part of that department. Note that this requires:
Some user groups with assigned users to exist
A multivalue responsibility attribute called 'Responsible department' for information systems with responsibility literals associated to those user groups
This allows users to "transfer responsibility", i.e. change the responsible department from theirs to some other department.
'use strict';
api.subscribeFor('InformationSystem', checkDepartment);
/**
* @param {BuildingBlockType} buildingBlockType
* @param {ChangeEvent} event
*/
function checkDepartment(buildingBlockType, event) {
for (var i = 0; i < event.length; i++) {
var change = event[i];
var bb = api.datamodel.findByTypeAndId('InformationSystem', change.id);
var user = bb.getValue('lastModificationUser');
var loginName = user.substring(user.indexOf("(")+1, user.length-1);
var lastModificationUser = api.user.get(loginName);
// First make sure we have the responsible departments before the change
// If this wasnt done, only administrators could change the responsible department, which might not be desirable
var departments = bb.getValues('Responsible department');
for (var j = 0; j < change.buildingBlockChanges.length; j++) {
var bbChange = change.buildingBlockChanges[j];
// If the responsible department was changed, we have to 'restore' its old state
if (bbChange.persistentName === 'Responsible department') {
// So add the departments that were removed
for (var k = 0; k < bbChange.removed.length; k++) {
departments.push(bbChange.removed[k]);
}
// And remove those that were added
for (var k = 0; k < bbChange.added.length; k++) {
var index = departments.indexOf(bbChange.added[k]);
if (index > -1) {
departments.splice(index, 1);
}
}
}
}
// Only check if the user is in the right department if the BB actually has a responsible department
if (departments.length > 0) {
var responsible = false;
var deps = '';
for (var j = 0; j < departments.length; j++) {
if (j > 0) {
deps += ', ';
}
if (lastModificationUser.isInGroup(departments[j])) {
responsible = true;
}
deps += departments[j];
}
// If the user is not in the right department and also not an Admin, reject the changes
if (!responsible && !lastModificationUser.isInGroup('Administration')) {
api.rejectChanges('You are not part of the department responsible for this Information System. Please contact someone from ' + deps);
}
}
}
}