What's an API Got to Do with It? Using the Teamstudio APIs to Speed Up your Processes

By Brian Arnold

If are reading this, then you are quite likely a user of (or at least know about) our suite of developer tools. It is the foundation of Teamstudio and many of them have been available for over 20 years. Our tools are used by literally thousands of people every day. In fact, IBM uses our developer tools for the creation of many of the Notes templates that you currently are using.

If you are a user of our developer tools then you already know the power of what they can provide (e.g. reporting on the differences between two Notes databases, analyzing the design of a Notes database within your Notes client, performing a search & replace within your code, etc.).

But have your ever run into a situation where you wished you could run the tools in a background thread? Or maybe you need to process multiple Notes databases with the same options in the tool? Or you might even need to run a design audit on all of the Notes databases on a Domino server?

If you've ever run into any of the above situations (or any others similar) you might find the process to be a bit cumbersome, since the tools run in the foreground on the single Notes database that you selected. If you had to perform a design audit on hundreds or even thousands of Notes databases, it would take a very long time and would require your input for each Notes database being processed.

So how can you streamline these processes? This is where the developer tools' APIs can help. We actually use the APIs within one of our own applications, Build Manager, to trigger background processes that can perform design audits, search & replace of design and/or documents, and even perform a design difference check between two similar Notes database designs.

Not many people are aware of this, but five of our nine developer tools provide you with an API that can be called from LotusScript code. The five tools are:

  • Analyzer (Database Design Analysis Tool)

  • CIAO! (Source Code Control System)

  • Configurator (Code and Document Search and Replace)

  • Delta (Database Design Comparison & Merge Tool)

  • Validator (Database Auditing and Reporting Tool)

If you use any of these products and have experienced a time when you needed to use the product on multiple Notes databases at the same time, then this blog post is for you!

The following information will get you on the track to creating some awesome companion utilities that will work with the developer tools that you already own.

With every installation of our developer tools you also receive a series of LotusScript libraries which contain the APIs that you can use. The most common method for using the APIs is to copy the required LotusScript libraries into your own application.

All of the required LotusScript libraries for the Analyzer, Configurator, Delta and Validator APIs can be found in the Teamstudio Reports template (tmslogs.ntf). The API for CIAO! can be found in the CIAO! Configuration template (ciao.ntf). These templates were deployed into your local IBM Notes data directory during the installation of the developer tools.

When you open the template in your IBM Domino Designer client and switch to the list of LotusScript libraries you will find the following which contain the APIs for the products:

TMSLOGS.NTF

  • Analyzer

  • Configurator

  • Delta

  • ValidatorUtil

CIAO.NTF

  • CIAO

As you can see, we attempt to make it as easy as possible to understand what to use. In the case of the CIAO! API there are a number of other script libraries within that template, but they are for the other actions that occur within the CIAO! configuration database.

We also provide full documentation on the APIs on our Teamstudio Documentation website. Here are direct links to the documentation for each:

For the remainder of this article I will be focusing on the Analyzer API. The other APIs are similar in their implementation, but since Analyzer is one of our most popular applications I will focus on using its API.

For the integration I created a new Notes database and copied the Analyzer script library from the above-mentioned template. Now I can start building the functionality that I need to run Analyzer in a "bulk process".

My next step is to create a new form. For this I named the form "Bulk Analysis". Now I need to create the fields that will contain my choices for what I want Analyzer to do. The fields I added are as follows:

table1.png

Now that we have the fields we need, we can start with the code. Before we can save the form we will need to make sure that the "ErrorList" field has a formula as it is a computed field. For this, we want it to compute to itself. This is most easily done by using the @Formula "@ThisValue". I use this @Formula in case I want to change the field name in the future; I won't need to remember to change the @Formula with this code.

The only other field that we need to supply some information for is the "Elements" field. With this field I want to list all of the design elements that can be analyzed. The Analyzer API understands a set of "Context Filters" which provide a list of all the design elements that Analyzer can run against. The list and more information about the Context Filters can be found in our documentation here.

To help you out, below you will find the formula I used for my choices:

"Agent|-ag":
"Applet|-ap":
"Component|-co":
"Composite Application|-ca":
"Custom Control|-cc":
"Data Connection|-dc":
"Database Icon|-ic":
"Database properties|-*":
"Database script|-ds":
"DB2 Data Access Views|-dv":
"File Resource|-fi":
"Folder|-fl":
"Form|-fm":
"Frameset|-fr":
"Help About|-ha":
"Help Using|-hu":
"Image|-im":
"Navigator|-na":
"Outline|-ou":
"Page|-pa":
"Profile Documents|-pr":
"Replication Formula|-rf":
"Script Library|-sl":
"Shared Action|-ac":
"Shared Column|-sc":
"Shared Field|-sh":
"Style Sheet|-ss":
"Subform|-sf":
"Theme|-th":
"V3 Macro|-v3":
"View|-vi":
"Webservice Consumer|-wc":
"Webservice Provider|-ws":
"Wiring Properties|-wp":
"XPage|-x"

If your plan is to always analyze ALL design elements then you will not need this field as we can pass an empty string into the API to indicate that we want to analyze everything in the Notes database.

Now comes the fun part; using the API. For Analyzer we provide four different API calls:

table2.png

For my needs I will be using the "DEANAnalyze2W32" and "DEANStringLoadW32" API calls as I want to be able to analyze selected design elements and I also want to report on any errors.

For the purposes of this article I am going to code everything within a single button on the form. For a more robust, reusable solution I would recommend using a new LotusScript library.

Once I have a new button on the form, I label it "Perform Bulk Analysis". At this point my form looks like this in Domino Designer:

Now it's time to add some code! The first thing we need to do is configure the button so that we can add LotusScript. When you click on the button you should see the programming pane below it. In the button options, select "LotusScript":

Now we need to reference the Analyzer script library. Since all of the code will be created in the button we can reference it from there. In the "Object" list to the left of the code pane click on "(Options)" and then enter the following code:

Use "Analyzer"

Now we can make references to the Analyzer API. Our next step is to write some LotusScript code in the button's "Click" event to get the values from the form. This is very simple code as you can see below:

Dim ws As New NotesUIWorkspace
Dim ui As NotesUIDocument
Dim note As NotesDocument
Set ui=ws.Currentdocument
Set note=ui.Document 

Now that we have a handle on the values that you will be entering into the form we need to make sure that the fields have the required values. There's no need to allow them to run the API call if it will inevitably fail. For this we will be making sure that all of the editable fields have a value. If you want to add more validation (such as server availability, path structure, output database existence, etc.) this is where you would add that type of validation. For now, I'm going to assume that you will be entering the values correctly!

In an effort to keep my code as clean as possible I created a function to perform the field validation. The validation code that I created is as follows:

Function ValidateFields (note As NotesDocument) As Boolean
	Dim msg As String
	If note.GetFirstItem("OutputServer").Text="" Then msg=Chr(10)+Chr(9)+"Analysis Output Server"
	If note.GetFirstItem("OutputPath").Text="" Then msg=msg+Chr(10)+Chr(9)+"Analysis Output Path"
	If note.GetFirstItem("SourceServer").Text="" Then msg=msg+Chr(10)+Chr(9)+Source Server"
	If note.GetFirstItem("SourcePath").Text="" Then	msg=msg+Chr(10)+Chr(9)+"Source Path"
	If note.GetFirstItem("Elements").Text="" Then msg=msg+Chr(10)+Chr(9)+Design Elements (select at least one)"
	If msg<>"" Then 
		Messagebox "The field(s) listed below need to have a value. Please provide a value and try again"+Chr(10)+msg
		Exit Function
	End If	
	ValidateFields=True
End Function

As you can see we are presenting a list of all fields that do not meet the basic validation requirements and presenting that list once. We could just as easily have created an @Formula and used it in the field's input validation, but I think it is better to present the user with a full list of the problems, instead of one at a time, which is what happens when using the input validation formula.

Now all we need to do is add a call to this new function in the button's "Click" event like this:

	If Not ValidateFields(note) Then Exit Sub

So now we have a handle on the values that the user enters, we've created validation code and now we can start using the API call (finally!). For this part I need to set up a series of variables to hold the values that were entered on the form, some additional variables to access the databases being analyzed, and also to setup return values from the Analyzer API calls.

Then I have to construct the string value that will represent the Analyzer output database location which will be passed into the API call. I also need to create a loop that will trigger the API call for each database that is listed in the "SourcePath" field (as shown below):

 	Dim outputServer As String
	Dim outputPath As String
	Dim outputDB As String
	Dim sourceServer As String 
	Dim paths As Variant
	Dim sourceDB As String
	Dim elements As Variant
	Dim x As Integer
	Dim status As Long
	Dim szBuffer As String*255
	Dim strErr As String
	Dim errors As String
	Dim session As New NotesSession
	Dim testdb As NotesDatabase
	outputServer=note.GetFirstItem("OutputServer").Text
	outputPath=note.GetFirstItem("OutputPath").Text
	sourceServer=note.GetFirstItem("SourceServer").Text
	paths=note.GetFirstItem("SourcePath").Values
	elements=note.GetFirstItem("Elements").Values
	If outputServer<>"" Then outputDB=outputServer+"!!"+outputPath Else outputDB=outputPath
	For x=0 To Ubound(paths)
	Next

Before we start using the Analyzer API calls (they're coming up next!) we also need to add some code to make sure that the databases that were entered into the "Source Paths" field actually exist and that the user has the rights to access them. This code is also very straightforward.

One note about this code; if you are a seasoned LotusScript coder, you are going to notice a "Bad Coding Practice" in this section, namely the use of "Resume Next". Yes, I know that this should rarely if ever be used, but for this example I do use it to trap any kind of problems that might occur when the code attempts to access the source database. Sometimes it just needs to be used.

Within the "For" loop I have added the following code which checks for the existence of the Notes database by attempting to instantiate it in a NotesDatabase object. If it exists and the user can access the Notes database we then need to setup the string value that is needed for the source Notes database location which will also be passed into the API call:

For x=0 To Ubound(paths)
		On Error Resume Next	' we need this here so that we can get past any problems accessing the database
		If sourceServer="" Then
			Set testdb=session.GetDatabase("",paths(x))
		Else
			Set testdb=session.GetDatabase(sourceServer,paths(x))
		End If
		If Not testdb.IsOpen Then Call testdb.Open("","")
		If Not testdb.IsOpen Or Err<>0 Then 
			errors=errors+Chr(10)+"DB: "+paths(x)+", ERROR: Cannot access database"
			Err=0
			Goto nextDB
		End If
		If sourceServer<>"" Then sourceDB=sourceServer+"!!"+paths(x) Else sourceDB=paths(x)
nextDB:
	Next

Now we are truly ready to use the Analyzer API. As mentioned earlier, I will be using the "DEANAnalyze2W32" and "DEANStringLoadW32" API calls. For more information on these calls you can visit our Documentation site at the following URLs:

The first API call is to trigger Analyzer to perform the analysis of the Notes database in the loop. This is the "DEANAnalyze2W32" API call. For this we need to use the following variables that we’ve already setup:

  • outputDB

  • sourceDB

  • elements

We will also be passing into the call two flags. The flags are what control how the Analyzer application will interact with the user. In our case we want it to run silently and also create the Analyzer output database if it does not already exist. A full list of the flags that can be used can be found on our documentation site here.

Once you have all of the variables set, the API call is very straightforward. Here is the initial API call which needs to be placed immediately after we setup the string variable for the sourceDB:

	status = DEANAnalyze2W32(_
	sourceDB,_               'database to analyze
	outputDB,_                'analysis database for output
	"Analyzer Report",_  'title for output database
	"",_                               'use default template
	elements,_                 'analyze filters / the list of design elements
	DBDEAN_FLAG_DEFAULT+DBDEAN_FLAG_SILENT) 'create analysis database if it doesn't exist and run silently

As you can see the API call is the least amount of code that we have created so far. The rest of the code is really a lead up to the API call.

The next API call is to "DEANStringLoadW32". This API will convert any error codes that the Analyzer API call returns into a readable format, since the first API call will return a Long value. The "DEANStringLoadW32" contains a mapping of the Long values to their textual equivalents. If the first API call returns a value that is not zero (0) then we know there was a problem. This code needs to be immediately after the first API call:

	If status<>0 Then
		Call DEANStringLoadW32( status, szBuffer, 255 )
		strErr = Left$( szBuffer, Instr( szBuffer, Chr(0) ) - 1 ) 	'Convert the string to a readable string
		errors=errors+Chr(10)+"DB: "+paths(x)+", ERROR: "+strErr
		status=0
	End If

The final piece to this code is to report on any errors that may have occurred within the API calls. You may have noticed that there are two lines in the code that are capturing errors as they occur into a variable called "errors". Outside of the "For" loop I will check to see if that variable is an empty string. If it is not then I will transfer any errors into the "ErrorList" field on the form and then finally do a quick refresh of the form UI:

	If errors<>"" Then note.ErrorList=errors
	Call ui.Refresh

At this point you should have a fully functional batch process for analyzing multiple Notes databases at the same time. My form looks like this when in use (basic I know, but functional):

Of course, there are lots of ways to make this more functional and easier for the end user to use, like adding a server and database picker (so the user doesn't enter the information incorrectly), using an agent that runs the "Bulk Analyses" code in a background process (so your Notes client is not held hostage by the process), a more styled interface so it doesn't look like a Notes 3 form, etc. Those improvements I leave up to you.

If you have comments or questions, please leave them below.

Happy coding!