Skip to content

Make Requests to Domino

Info

This how-to assumes you are familiar with setting up a project either manually or via VoltScript Dependency Management. If you are not, follow the Intro to VoltScript Tutorials.

Introduction

When coding LotusScript, Domino access is expected and set up automatically via NotesSession object and specific user access automatically applied. VoltScript is middleware and may or may not require access to Domino data to perform its middleware functions. So the relevant VoltScript Extension needs to be added, as for any other VoltScript extension. If Notes or Domino and a 64-bit "nlsxbe.dll" (for Windows, "liblsxbe.so" for Linux) are on the same machine as the VoltScript runtime, you may be able to use NotesLSX. Otherwise connection to Domino will be via Domino REST API, and KeepVSE.

Note

The recommended interface is Domino REST API.

2 Using NotesLSX

Info

nlsxbe.dll is not supported for dependency management. If you wish to use this, you will need to manually add it to your seti.ini. Because the dll is unlikely to be in the same location on all environments, it's not recommended to use VoltScript code accessing NotesLSX as a project deployed to another environment.

Warning

  • VoltScript is 64-bit, so a 64-bit NotesLSX must be used.
  • NotesUI classes won't be available with NotesLSX. They would not work anyway. Errors and constants will also not be available unless you include a Use statement mapping to the location of the relevant .lss file.

The following line in the relevant section of your seti.ini would map the label "NotesLSX" to the standard Windows location of the dll:

NotesLSX=C:/Program Files/HCL/Domino/nlsxbe.dll

You can then use UseVSE "*NotesLSX" to access Domino as the current ID user.

Warning

On Windows, you will need to shut down the program (Domino or Notes) associated with nlsxbe.dll before running the VoltScript code. Otherwise, you will only have access to the NotesSession object and any attempt to access databases will fail. Running two VoltScript scripts at the same time may also fail, for the same reasons.

VoltScript dependencies

Incorporating KeepVSE is straightforward. You just need to add the following JSON object to the vsesDependencies element in your atlas.json.

        "KeepVSE": {
            "library": "25280",
            "version": "1.0.1",
            "module": "25884/20851",
            "repository":"volt-mx-marketplace"
        }

You'll need to add to your repositories object in the atlas.json of your project:

        {
            "id": "volt-mx-marketplace",
            "type": "marketplace",
            "url": "https://community.demo-hclvoltmx.com/marketplace/asset"
        }

To use the extension in your script, enter UseVSE "*KeepVSE".

You will probably also want to incorporate JsonVSE.

Domino REST API

KeepVSE is designed to access Domino REST API. The VoltScript Extension is only designed for data access, not database or user management. So only the data APIs are exposed in KeepVSE. Of course, this doesn't preclude you making REST calls via WebVSE to other Domino REST API endpoints.

Info

This how-to guide assumes familiarity with Domino REST API terminology. For more information on the Domino REST API terminology, see the Domino REST API documentation

It's important to bear in mind performance because the VoltScript code will be running as middleware, making individual REST service calls for most APIs. The following diagrams demonstrate REST service calls for creating a document and updating an existing document:

Creating Document

Updating Document

KeepServer class

The KeepServer class is the entrypoint for any Domino REST API calls. You need to specify the serverURL, which is protocol + server name + "api/v1". This is the same URL that appears under "Servers" on the Swagger REST API:

Domino REST API Server

After adding the serverURL you will need to call login() passing a username and password. The following code will login to the server, assuming the variables serverName, userName and password have been set.

Function login(server as KeepServer, userName as String, password as String)
    server.serverURL = SERVER_NAME
    Call server.login(userName, password)
End Function

This will give a logged in session to the Domino REST API as the passed user, which can be verified with server.connected.

At the end of the code you can call server.logout to invalidate the created JWT token.

Warning

There are no APIs to access the JWT token from an authentication request. But the bearer token will be written to the libcurl.log. So it's recommended practice to logout at the end of the code. This can be done with Try/Catch/Finally:

    Try
        'Login and run your code
    Catch
        Print "Error " & Error() & " on line " & Erl()
    Finally
        If (Not server is Nothing && server.connected) Then server.logout
    End Try

KeepScope class

The KeepScope class is the entrypoint for data operations on a specific Domino REST API scope, including accessing view and document data, and performing DQL queries. All operations will require the Domino REST API to be configured with the relevant resources and functions exposed.

Accessing a scope

The following code can return the scope:

Function getScope(server as KeepServer, scopeName as String) as KeepScope
    Return server.getScope(scopeName)
End Function

Info

If an error occurs, Error() will return the JSON object that Domino REST API returns, plus "ERROR: http response code = " plus the HTTP error code.

The full JSON for the scope can be retrieved, but there are also helper methods for listing forms, views, and agents that are available.

Note

You will probably want to use JsonVSE to more easily parse the JSON content from Domino REST API.

Accessing list / view data

One of the primary entrypoints to document data is Domino views, or in the Domino REST API terminology "lists". Because VoltScript is primarily targeted at LotusScript developers, KeepVSE uses the term "view". Typically the developer will know the name of the list to use, but if not then the developer can use KeepScope.listAllViews() or use other means exposed via Domino REST API.

There are three functions available for accessing view data:

  • getViewEntries() for accessing generic view entries.
  • getViewCategories() for retrieving information about top-level categories.
  • getViewEntriesByKey() for retrieving entries within a category or sub-category.

The following code will get the first 5 entries from a view:

Function getViewEntries(server as KeepServer, scopeName as String, viewName as String) as JsonObject
    Dim scope as KeepScope
    Dim parser as New JsonParser()
    Set scope = server.getScope(scopeName)
    Call parser.loadFromJSON(scope.getViewEntries(viewName, 5))
    Return parser.getRootObject()
End Function

Basic CRUD Operations

Creation

Creating a document requires knowing that a form is exposed and the the items expected for the "default" mode, which is the mode used when creating a document. This can be accessed from the KeepScope.GetFormInfo() API. This includes all configuration information for the Form as defined in Domino REST API. The following code can return the "default" mode:

Function getDefaultMode(server as KeepServer, scopeName as String, formName as String) as JsonObject
    Dim scope as KeepScope
    Dim parser as New JsonParser()
    Dim jsonObj as New JsonObject
    Set scope = server.getScope(scopeName)
    Call parser.loadFromJson(scope.getFormInfo(formName))
    Set jsonObj = parser.getRootObject()
    ForAll mode in jsonObj.getChild("formModes").getChildren()
        If ("default" = CStr(mode.getChild("modeName").scalarValue)) Then
            Print mode.toString(True)
        End If
    End ForAll
End Function

Warning

It's recommended to add defensive coding and error handling as required.

Info

For fields, there is an "externalName" which is what KeepVSE needs to use. If a field's fieldAccess is "RW", it should be passed when creating a document.

Once the fields required and formats are known, you are ready to create the document. The following code will create a document and save it to Domino REST API:

Function createCustomer(server as KeepServer, scopeName as String) as KeepDocument
    Dim scope as KeepScope
    Dim jsonObj as New JsonObject
    Dim doc as KeepDocument
    Set scope = server.getScope(scopeName)
    Call jsonObj.insertValue("Form", "Customer")
    Call jsonObj.insertValue("Color", "Red")
    Call jsonObj.insertValue("first_name", "John")
    Call jsonObj.insertValue("last_name", "Doe")
    Call jsonObj.insertValue("gender", "Male")
    Call jsonObj.insertValue("Pet", "Cockapoo")
    Set doc = scope.createDocument(jsonObj.toString(false))
    Call doc.save("", "")
    Return doc
End Function

Warning

The createDocument API doesn't make a call to Domino REST API. It just creates a VoltScript object. The save() call submits the data to Domino REST API.

Note

The second parameter of KeepDocument.save() is ignored when creating documents, because Domino REST API only allows documents to be saved at "default" mode.

When creating multiple documents, it's important to remember to re-initialize the KeepDocument each time, otherwise you will be updating the same document:

Sub createCustomers(server as KeepServer, jsonObjs as Variant)
    Dim scope as KeepScope
    Dim doc as KeepDocument
    Set scope = server.getScope("demo")
    ForAll json in jsonObjs
        Set doc = scope.createDocument()
        doc.JsonValue = json.toString(false)
        Call doc.save("", "Customer")
    End ForAll
End Sub

Retrieval

Retrieving a document is done using KeepScope.getDocument(). Querystring parameters that can are exposed in Domino REST API are also available in as arguments of the getDocument() function. The following code can retrieve a document with no rich text manipulation, computing default value and input translation and validation formulas, and returning metadata.

Function getDocument(server as KeepServer, scopeName as String, unid as String) as KeepDocument
    Dim scope as KeepScope
    Dim doc as KeepDocument
    Set scope = server.getScope(scopeName)
    Set doc = scope.getDocument(unid, False, False, True, True, "default")
    Return doc
End Function

Warning

Programmatic manipulation of rich text content can be risky. Different rich-text editors have different abilities to accept / output richly-formatted content. So even with no changes, there are no guarantees the user interface will faithfully round-trip the content outputted.

Update

Domino REST API uses different Form Access Modes for managing what fields are available for read / update at particular stages in the workflow. Consequently, it's important to know the Form Access Modes available and you will need to specify the required Form Access Mode when updating the document. You may already have identified this with KeepScope.getFormInfo(), passing the form name. KeepScope.GetDocumentModes() will provide the Form Access Modes when passing an UNID:

Function getDocumentModes(server as KeepServer, scopeName as String, unid as String) as JsonObject
    Dim scope as KeepScope
    Dim parser as New JsonParser()
    Set scope = server.getScope(scopeName)
    Call parser.loadFromJSON(scope.getDocumentModes(unid))
    Return parser.getRootObject()
End Function

Then to update the document, replace the JSON in KeepDocument.JSONValue and re-save, with the relevant Form Access Mode:

Function updateDocument(server as KeepServer, scopeName as String, unid as String) as KeepDocument
    Dim parser as New JsonParser()
    Dim jsonObj as JsonObject
    Dim scope as KeepScope
    Dim doc as KeepDocument
    Set scope = server.getScope(scopeName)
    Set doc = scope.getDocument(unid)
    Call parser.loadFromJSON(doc.JsonValue)
    Set jsonObj = parser.getRootObject()
    Call jsonObj.insertValue("Color", "Red")
    doc.JsonValue = jsonObj.toString(False)
    Call doc.save("", "default")
    Return doc
End Function

Delete

Deleting a document can be done by passing either the unid or a KeepDocument object. Both require passing a Form Access Mode name at which the current user has permissions to delete documents.

Warning

If the deletion isn't successful, a 400 error will be returned, with further details in the error message.

The following code will delete a document using just its UNID:

Sub deleteDocumentByID(server as KeepServer, scopeName as String, unid as String)
    Dim scope as KeepScope
    Dim doc as KeepDocument
    Set scope = server.getScope(scopeName)
    Call scope.deleteDocumentByID(unid, "admin")
End Sub

There may be occasions where you want to act upon a document first, either checking item values or copying it somewhere, for example to an archive. The following code will retrieve a document, create a copy and delete the original:

Function copyAndDeleteDocument(server as KeepServer, scopeName as String, unid as String) as KeepDocument
    Dim scope as KeepScope
    Dim doc as KeepDocument
    Dim newDoc as KeepDocument
    Set scope = server.getScope(scopeName)
    Set doc = scope.getDocument(unid, False, False)
    Set newDoc = scope.createDocument()
    newDoc.jsonValue = doc.jsonValue
    Call newDoc.save("", "default")
    Call scope.deleteDocument(doc, "admin")
    Return doc
End Function

Bulk (C)RU(D) operations and DQL

Bulk operations are done from the KeepScope and, because of variable return data, return JSON strings. Bulk operations are more efficient because it will make a single REST service call to Domino REST API instead of one REST service call for each document.

Note

There is currently no operation to perform bulk creation and deletion.

Retrieval by UNID

Info

At this time Domino REST API doesn't provide the ability to pass a Form Access Mode when retrieving documents, so they will be retrieved at "default" mode.

If you have a list of UNIDs for documents, you can use KeepScope.getDocuments() to retrieve them. The following code will take an array of UNID strings and return an array of JSON strings, where each string corresponds to the document JSON.

Function getDocuments(server as KeepServer, scopeName as String, unids() as String) as Variant
    Dim scope as KeepScope
    Dim docs as Variant
    Set scope = server.getScope(scopeName)
    docs = scope.getDocuments(unids)
    Return docs
End Function

Note

String arrays can be passed as arguments into functions. But a variable declared as a string array can't be populated via a function. So the following code won't compile, with the error "Illegal reference to array or list":

Dim docs() as String
docs = getDocuments(server, "demo", unids)

Similarly a function cannot have a return value defined as a String array. Instead, both variable and function return type must be defined as Variant.

Retrieval via DQL

DQL provides the ability to return documents based on a query. The following code would return the first five Customer documents where the color is "Blue":

Function performDqlQuery(server as KeepServer, scopeName as String) as String
    Dim scope as KeepScope
    Dim query as New JsonObject
    Dim vars as New JsonObject

    Set scope = server.getScope("demo")
    Call query.insertValue("maxScanDocs", 5000)
    Call query.insertValue("maxScanEntries", 2000)
    Call query.insertValue("mode", "default")
    Call query.insertValue("noViews", False)
    Call query.insertValue("timeoutSecs", 300)
    Call query.insertValue("query", "form = 'Customer' and Color = ?Color")
    Call query.insertValue("viewRefresh", False)
    Set vars = new JsonObject()
    vars.label = "variables"
    Call vars.insertValue("Color", "Blue")
    Call query.insertObject(vars)
    Return scope.dqlQuery(query.toString(False), "execute", 5)
End Function

Tip

Use JsonVSE to build the query rather than trying to build it as a String. If you get an error message for invalid JSON, print out the JSON and cross-reference it with / try it in Swagger on the relevant server.

Note

KeepScope.DQLQuery() can also be used to perform other DQL verbs - "query" and "explain".

Update via DQL

Bulk update is performed via a DQL query, a list of items to update and a Form Access Mode with which to perform the update. The KeepScope.bulkUpdate() method can either return the full JSON of the documents at the relevant mode, or a JSON object that contains a summary of the documents updated. The following code will update Customers where Color is "Red", changing the color to "Maroon". It will return all values from the underlying documents.

Function updateDocuments(server as KeepServer, scopeName as String) as String
    Dim scope as KeepScope
    Dim replaceItems as New JsonObject()

    Set scope = server.getScope("demo")
    Call replaceItems.insertValue("Color", "Maroon")

    Return scope.bulkUpdate("form = 'Customer' and Color = 'Red'", 5000, True,_ 
        replaceItems.toString(false), "default", True)
End Function

If false had been passed as the last parameter of KeepScope.bulkUpdate() the result would have looked like this:

[
{"statusText":"OK","status":200,"message":"Document 2B057308D23ED5AD00258A210046664B updated","unid":"2B057308D23ED5AD00258A210046664B"},
{"statusText":"OK","status":200,"message":"Document A1057308D23ED5AD00258A210046527F updated","unid":"A1057308D23ED5AD00258A210046527F"}
]

Attachments

Add an attachment

Adding an attachment is done by using KeepDocument.createAttachment(). You need to pass the filepath of the attachment to add and an optional name of a field in which to place the attachment. The following code will create an attachment in a field called "RichText":

Function addAttachment(doc as KeepDocument, filePath as String) as String
    Return doc.createAttachment(CurDir() & "/libs/functions.vss", "RichText")
End Function

Warning

The document corresponding to the KeepDocument object needs to be in the database before the attachment is added. This means if the KeepDocument was created via KeepScope.createDocument(), you must populate the document with JSON and call KeepDocument.save() prior to adding the attachment.

Download an attachment

Downloading an attachment isn't yet implemented.

The complete implementations of the code snippets are available on GitHub.