Automating Work Assignments with iPlugin and Workflow Management Software

Workflow Management software can automate the Application Readiness process to make it easier and faster to repackage, test, and deploy software.  As with any workflow process, it is necessary to assign specific people to each request to carry out the work, and in many environments, those assignments can also be automated based on a number of criteria.

This is helpful when you need to assign reviewers, such as an application owner, which varies between different business units. In this example, I will show you how to automatically assign people to workflow requests based on their business unit.

Workflow Manager API for Work Assignment

Flexera Workflow Manager provides the AdminStudio Public API, which is a set of functions that operate on workflow requests.  The first API we need is the SetWorkAssignments API.  Its declaration is:

void SetWorkAssignments(CompanyType eCompanyType, string RoleName, string UserName);

The SetWorkAssignments API assigns one user to a role.  The CompanyType argument is an enumeration that indicates whether the company is the “Requester” company or the “Servicer” company.  Notice that this API does not return a value, so it fails silently if it can’t assign a user to a role.  This method will also throw an exception in some cases.

The second API that we need is the GetWorkAssignments API.  This is the API that we will use to confirm that the assignment was successful.  Its declaration is:

Hashtable GetWorkAssignments(CompanyType eCompanyType);

The return type for this API is a Hashtable, which is similar to an array.  The Hashtable contains a list of roles and the people assigned to them.  We can look at a specific row in the Hashtable to see if the user has been assigned to a role.

The last API that we will need is the GetDataElement API which can get the value of a data element in a workflow request.  Its declaration is:

String GetDataItem(string DataItemName, string DataGroupName);

The return value for the GetDataItem API is the value of the data element.

Table to Drive Work Assignment

To assign roles based on our business rules, in this case the business unit, we will define a table that contains the criteria and the users that we want to assign to a particular role.  In this example, we have a Business Unit data element for each workflow request, and our table lists the business units with the designated users who will work on the request.

Table 1: An example table to control the work assignment based on the “Business Unit” data element in the request.

Business_UnitUserName
ConsultingACRESSO\StevenBurk
EngineeringACRESSO\AlanBurns
ServicesACRESSO\JonBarag

It is possible to store this table in the AdminStudio database using a name that distinguishes it from the other AdminStudio data, such as “Custom_BU_Reviewers”.  We have found that it works better to store tables like this one in a separate database so that it is independent of any schema updates for AdminStudio.  Another reason to use a separate database is because you may create other custom tables to support other work assignments or other custom functionality.

The iPlugin DLL can access the table in another database if you provide the connection string in the web.config file.  For example, you could store it in an App Setting:

<add key="LookupDBConnectionString"
value="data source=MYSQLSERVER;integrated security=SSPI;initial catalog=AdminStudioLookup" />

With this setting in the web.config file, the iPlugin DLL can retrieve it with a call in the class constructor:

szLUConnectionString = ConfigurationManager.AppSettings["LookupDBConnectionString"];

Function to Do Work Assignment

Normally we do work assignment shortly after someone submits the request, so we would want to call our work assignment function from the iPlugin AppWorkflowStepComplete event and tie it to the completion of the “Enter Data” step, for example.  Listing 1 shows the AppWorkflowStepComplete event and the call to the AssignOneRole method which only runs on the completion of the “Enter Data” step.

Listing 1: The AppWorkflowStepComplete event that calls the AssignOneRole method

public void AppWorkflowStepComplete(string strAppId, string strAppXMLInfo) {
	string szContext = "AppWorkflowStepComplete";
	Logger.WriteLog(szContext, "--------------------------------------------------------------");
	AppInfo ai = new AppInfo(strAppXMLInfo);
	ApplicationRequest AR = GetApplicationRequest(ai);
	Logger.WriteLog(szContext, "    Application Name: {0}", ai.AppLName);
	try {
		Logger.WriteLog(szContext, "    Current Phase:    {0}", ai.CurrentPhaseName);
		Logger.WriteLog(szContext, "    Current Step:     {0}", ai.CurrentStepName);
		// Request / Enter Data
		if (ai.CurrentPhaseName == "Request" && ai.CurrentStepName == "Enter Data") {
			// Get the Business Unit from the workflow request.
			string szBU = AR.GetDataItem("Business Unit", "Support");
			Logger.WriteLog(szContext, "Business Unit: {0}", szBU);
			// Get the username for the Business Unit Reviewer.
			string szQuery = @"SELECT Username FROM Custom_BU_Reviewers WHERE Business_Unit = @Param1";
			string szBUReviewer = ExecuteSimpleSQLQuery(szLUConnectionString, szQuery, szBU).Trim();
			// Assign the user to the Business Unit Reviewer role.
			AssignOneRole(ref AR, CompanyType.Requester, "Business Unit Reviewer", szBUReviewer);
		}
	}
	catch (Exception ex) {
		Logger.WriteLog(szContext, "EXCEPTION: {0}", ex.Message);
		if (ex.InnerException != null) {
			Logger.WriteLog(szContext, "Inner Exception: {0}", ex.InnerException.Message);
		}
	}
	return;
}

Listing 2 shows the details of the AssignOneRole function which relies on the SetWorkAssignments API to actually do the assignment.  Note that after calling SetWorkAssignments, the function immediately calls GetWorkAssignments to confirm that the role was assigned.

Listing 2: The AssignOneRole function

private void AssignOneRole(ref ApplicationRequest AR, CompanyType CT, string szRole, string szPerson) {
	string szContext = "AssignOneRole";
	if (szRole.Length < 1) {
		Logger.WriteLog(szContext, "Error: The Role parameter was empty.");
	}
	else if (szPerson.Length < 1) {
		Logger.WriteLog(szContext, "Error: The person parameter was empty.  " +
		"Could not assign anyone to the {0} role.", szRole);
	}
	else if (PersonExistsInActiveDirectory(szPerson) == false) {
		Logger.WriteLog(szContext, "Error: Could not find \"{0}\" in Active Directory.  " +
		"Could not assign \"{0}\" to the \"{1}\" role.", szPerson, szRole);
	}
	else {
		try {
			Logger.WriteLog(szContext, "Assigning \"{0}\" to \"{1}\".", szPerson, szRole);
			AR.SetWorkAssignments(CT, szRole, szPerson);
			// Confirm that the assignment was successful.
			Hashtable haAssigns = AR.GetWorkAssignments(CT);
			if (haAssigns[szRole] == null || haAssigns[szRole].ToString() == String.Empty) {
				Logger.WriteLog(szContext, "Error: The role {0} is not assigned.", szRole);
			}
			else {
				Logger.WriteLog(szContext, "Success.  The role {0} is now assigned to {1}.",
				szRole, haAssigns[szRole].ToString());
			}
		}
		catch (Exception ex) {
			Logger.WriteLog(szContext, "EXCEPTION: {0}", ex.Message);
		}
	}
}

Listing 3 shows the GetApplicationRequest function, which is a helper function that gets the ApplicationRequest object for the current request from Workflow Manager.  The ApplicationRequest object contains the values of all of the data elements.

Listing 3: The GetApplicationRequest function

///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the application request for the current workflow.
/// </summary>
/// <returns></returns>
private AdminStudio.Public.WorkflowManager.ApplicationRequest GetApplicationRequest(AppInfo ai) {
	string szContext = "GetApplicationRequest";
	Logger.WriteLog(szContext, "Entering function.");
	AdminStudio.Public.Session.AesSession session = null;
	Logger.WriteLog(szContext, "session = null");
	try {
		Logger.WriteLog(szContext, "Trying to load session from web context.");
		session = AdminStudio.Public.Session.AesSession.LoadFromWebSession();
		Logger.WriteLog(szContext, "Obtained an AesSession object from LoadFromWebSession().");
	}
	catch (Exception ex1) {
		//  one more try
		try {
			Logger.WriteLog(szContext, "Exception: {0}", ex1.Message);
			Logger.WriteLog(szContext, "Trying to load session with user GUID \"{0}\"", ai.UserNameGuid);
			session = AdminStudio.Public.Session.AesSession.LoadFromWebSession(ai.UserNameGuid);
			Logger.WriteLog(szContext, "Obtained an AesSession object from LoadFromWebSession(\"{0}\").",
			ai.UserNameGuid);
		}
		catch (Exception ex) {
			Logger.WriteLog(szContext, "Exception: {0}", ex.Message);
		}
	}
	if (null == session) {
		Logger.WriteLog(szContext, "The AesSession is null.  Returning null.");
		return null;
	}
	Logger.WriteLog(szContext, "Loading an application request for application ID {0}.", ai.AppID);
	AdminStudio.Public.WorkflowManager.ApplicationRequest ar =
	AdminStudio.Public.WorkflowManager.ApplicationRequest.Load(session, ai.AppID);
	Logger.WriteLog(szContext, "Returning an ApplicationRequest object named '{0}'.", ar.Name);
	return ar;
}

By now you have probably noticed that there are a few other functions involved, such as the Logger.WriteLog functions that write messages to the log file.  You can find details for these functions in the source code for the iPlugin project example that is associated with this blog post.  For those who want to write their own automatic work assignment process, the iPlugin project file should give you a great start on your implementation.

More Information

You can find more information about the AdminStudio Public API at the Flexera Software HelpNet website (http://helpnet.flexerasoftware.com).

Category: General

Leave a Reply

Your email address will not be published. Required fields are marked *