Skip to main content
Skip table of contents

Plugin API examples

Setting a default attribute value

This script sets a default attribute value for a newly created information system.

JS
'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.

JS
'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.

JS
'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).

JS
'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:

JS
'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:

JS
'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:

JS
'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:

JS
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:

JS
'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.

JS
'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:

JS
'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:

JS
'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.

JS
'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.

JS
'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.

JS
'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.

JS
'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.

JS
'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.

JS
'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.

JS
"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.

JS
'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);
            }
        }
    }
}

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.