Understanding Maximo Integration Object Resource Ids

July 24, 2023

Introduction

When using Maximo Object Structures with the REST API you may have noticed that each object includes a unique resource reference URL returned as the href property. This URL contains the Maximo base URL, followed by oslc/os/{os_name} and finally a seemingly random group of letters and numbers, which uniquely identifies the Maximo resource. This resource Id is used when updating or deleting Maximo resources and for special cases such as adding attached documents.

This seemly random jumble of numbers and letters is not random at all. It can both be decoded to view its contents and also can be generated without having to first query Maximo. This is can be useful when performing updates and deletes via the REST API since it saves you the extra step of of first querying the system for the record. It is also just good to better understand the REST API and make it little bit less mysterious.

In this post we will show how to decode the Object Structure resource Id and then how to generate an resource Id for Maximo objects.

Please note that the resource Ids are an internal implementation detail of the REST API. While likely to remain stable and the information in this post accurate, this is not guaranteed and you should not rely on that being the case.

Decode Object Id

To get started we will use the example of looking for work order 1148 in the BEDFORD site from the maximo.sharptree.io system. We want to get the WONUM, DESCRIPTION, and STATUS in return and will create the following query.

https://maximo.sharptree.io/maximo/oslc/os/mxwo?lean=true&oslc.where=wonum="1148" and siteid="BEDFORD"&oslc.select=wonum,description,status

We then get the following response. Note that the href property is https://maximo.sharptree.io/maximo/oslc/os/mxwo/_QkVERk9SRC8xMTQ4 and that the resource Id is _QkVERk9SRC8xMTQ4.

{
"member": [
{
"_rowstamp": "3277171",
"status_description": "Closed",
"description": "Install Centrifugal Pump",
"href": "https://maximo.sharptree.io/maximo/oslc/os/mxwo/_QkVERk9SRC8xMTQ4",
"status": "CLOSE",
"wonum": "1148"
}
],
"href": "https://maximo.sharptree.io/maximo/oslc/os/mxwo",
"responseInfo": {
"href": "https://maximo.sharptree.io/maximo/oslc/os/mxwo?lean=true&oslc.where=siteid=%22BEDFORD%22%20and%20wonum=%221148%22&oslc.select=wonum,status"
}
}

The resource Id of _QkVERk9SRC8xMTQ4 is an underscore (_) prefixed Base64 encoded String that contains a forward slash (/) delimited list of the object's Primary Column values in alphabetical order by attribute name. There are also several special cases for encoding the values, which include null values represented as ~NULL~, boolean values represented as 0 for false and 1 for true and any occurrences of a forward slash (/) replaced with "sp" with the double quotes.

Value Replacements

ValueReplacement
null~NULL~
boolean0 for false, 1 for true
/"sp"

The resulting Base64 encoded value has the equals symbol (=) replaced with a dash (-), the plus symbol (+) replaced with an underscore (_) and a forward slash (/) replaced with and tilde (~). This is to ensure that valid Base64 encoding characters do not conflict with reserved URL characters.

Resource Id Replacements

ValueReplacement
=-
+_
/~

For more information on Base64 the Wikipedia article provides a very good overview https://en.wikipedia.org/wiki/Base64.

You can verify this yourself by removing the leading underscore and replacing any dashes (-) with an equals symbol (=), underscores (_) with a plus symbol (+) and any tilde (~) with a forward slash (/) and then Base64 decoding the value.

There are numerous sites on the internet that provide utilities to decode Base64 text, but my favorite is the one provided by the Google Toolbox found here https://toolbox.googleapps.com/apps/encode_decode/. Taking the value from our query and removing the leading underscore we are left with a value of QkVERk9SRC8xMTQ4, place this in the text provided textbox, select Base64 Decode and click the SUBMIT link. The value will be decoded below entry textbox and will show BEDFORD/1148

Google Toolbox Base64 Decode

The values of BEDFORD AND 1148 correspond to the Primary Column values as defined in the System Configuration > Platform Configuration > Database Configuration application in Maximo. As seen in the following image, the SITEID and WONUM attributes are primary columns. Clicking the Primary Column table header will sort the attributes in primary column order and bring the primary columns to the top of the list.

Work Order Primary Columns

Encode Object Id

Understanding the contents of the resource Id allows us to create our own resource Id and use it to directly query Maximo. For our example we will use Purchase Order 1010 revision 0 in the BEDFORD site.

First we must identify the primary columns for the PO object, which can be done by navigating to the System Configuration > Platform Configuration > Database Configuration application and searching for the PO application and then clicking the Attributes tab. Clicking the Primary Column header sorts the attributes in Primary Column order and we can see that the primary columns for the PO object in alphabetical order are PONUM,REVISIONNUM and SITEID.

Purchase Order Primary Columns

Returning to the Google Apps Toolbox, select the Base64 Encode option and enter the PONUM, REVISIONNUM and SITEID separated by forward slashes (/), for our example this value is 1010/0/BEDFORD. Then click the SUBMIT link and the value MTAxMC8wL0JFREZPUkQ= is generated.

Google Toolbox Encode Base64

To create the query URL, the resource Id value is appended to an underscore (_) and the equals symbol (=) is replaced with a dash (-), with the result being _MTAxMC8wL0JFREZPUkQ-. This resource Id can now be used to directly query for Purchase Order 1010 with the lean query parameter set to true.

https://maximo.sharptree.io/maximo/oslc/os/mxpo/_MTAxMC8wL0JFREZPUkQ-?lean=1

Using a system populated with the standard Maximo Demo data, the following result is returned.

{
"ponum": 1010,
"revisionnum": 0,
"description": "General Plumbing Supplies",
"status": "APPR",
"siteid": "BEDFORD",
"ignorecntrev": false,
"customernum": "9X-567-9",
"totaltax4": 0.0,
"totaltax3": 0.0,
"purchaseagent": "NANKIN",
"totaltax5": 0.0,
"currencycode": "USD",
"totaltax2": 0.0,
"totalcost": 332.07,
"totaltax1": 21.49,
continued...
}

Automation Scripts

If you are interacting with a Maximo Integration Object in an automation script there are several helpful utility classes for working with Maximo Integration Objects and their resource Id values. The com.ibm.tivoli.maximo.oslc.OslcUtils class contains hundreds of static utility methods for interacting with a Mbo object and the REST API. Of particular interest for our topic is the recoverResourceId(String resourceId) method, which takes an encoded String resource Id and returns its decoded value.

The com.ibm.tivoli.maximo.oslc.provider.LocalURIGenerator class contains the getEncodedMboID(MboRemote mbo, OslcResourceInfo oslcResInfo) method that generates a resource Id for a given Mbo and OslcResourceInfo object. To use this method you need to obtain a OslcResourceInfo object for the Object Structure, fortunately this is available from the com.ibm.tivoli.maximo.oslc.provider.OslcResourceCache class with the following method OslcResourceCache.getInstance().getOslcResourceInfo({os_name}) where {os_name} is the object structure identifier.

IBM publishes the JavaDocs for every version of Maximo here https://ibm.ent.box.com/v/maximojavadocs. This is an invaluable resource for understanding how Maximo works and how to interact with the Maximo APIs. Bruno Portaluri also hosts the Maximo 7.6.1.2 JavaDocs on his website at https://bportaluri.com/wp-content/MaximoJavaDocs7612/.

We have reached out to IBM for permission to host the Maximo JavaDocs in a browsable format and will update this post with the URL when we receive a response.

Encoding a Resource Id

Putting all this together you can get the Object Structure resource Id for the Purchase Order in our example with the following code.

This example assumes it is being invoked from a web script request and that the implicit responseBody variable is available.

JavaScript

LocalURIGenerator = Java.type("com.ibm.tivoli.maximo.oslc.provider.LocalURIGenerator")
OslcResourceCache = Java.type("com.ibm.tivoli.maximo.oslc.provider.OslcResourceCache");
SqlFormat = Java.type("psdi.mbo.SqlFormat");
MXServer = Java.type("psdi.server.MXServer");
main();
function main() {
var poSet;
try {
poSet = MXServer.getMXServer().getMboSet("PO", userInfo);
var sqlFormat = new SqlFormat("ponum = :1 and siteid =:2 and revisionnum = :3");
sqlFormat.setObject(1, "PO", "PONUM", "1010");
sqlFormat.setObject(2, "PO", "SITEID", "BEDFORD");
sqlFormat.setInt(3, 0);
poSet.setWhere(sqlFormat.format());
var po = poSet.moveFirst();
if (po) {
var oslcResourceInfo = OslcResourceCache.getInstance().getOslcResourceInfo("MXPO");
responseBody = (new LocalURIGenerator().getEncodedMboID(po, oslcResourceInfo));
} else {
responseBody = "The purchase order for PO number 1010, revision 0 in site BEDFORD was not found.";
}
} finally {
if (poSet) {
poSet.close();
poSet.cleanup();
}
}
}

Jython

from com.ibm.tivoli.maximo.oslc.provider import LocalURIGenerator, OslcResourceCache
from psdi.mbo import SqlFormat
from psdi.server import MXServer
def main():
poSet = MXServer.getMXServer().getMboSet("PO", userInfo)
try:
sqlFormat = SqlFormat("ponum = :1 and siteid =:2 and revisionnum = :3")
sqlFormat.setObject(1, "PO", "PONUM", "1010")
sqlFormat.setObject(2, "PO", "SITEID", "BEDFORD")
sqlFormat.setInt(3, 0)
poSet.setWhere(sqlFormat.format())
po = poSet.moveFirst()
if po is not None:
oslcResourceInfo = OslcResourceCache.getInstance(
).getOslcResourceInfo("MXPO")
return LocalURIGenerator().getEncodedMboID(po, oslcResourceInfo)
else:
return "The purchase order for PO number 1010, revision 0 in site BEDFORD was not found."
finally:
if poSet is not None:
poSet.close()
poSet.cleanup()
responseBody = main()

Decoding a Resource Id

Doing the opposite this time, taking an Integration Object resource Id, we will decode the value.

This example again assumes it is being invoked from a web script request and the query parameter requestId is provided and available within the script.

JavaScript

OslcUtils = Java.type("com.ibm.tivoli.maximo.oslc.OslcUtils");
main();
function main() {
if (typeof request !== 'unknown') {
var resourceId = request.getQueryParam("resourceId");
if (!resourceId) {
responseBody = "The query parameter \"resourceId\" is required.";
return;
}
// remove the leading underscore if provided.
resourceId = (resourceId.startsWith("_") ? resourceId.substring(1) : resourceId);
responseBody = OslcUtils.recoverResourceId(resourceId);
}
}

Jython

from com.ibm.tivoli.maximo.oslc import OslcUtils
def main():
if request is not None:
resourceId = request.getQueryParam("resourceId")
if resourceId is None:
return "The query parameter \"resourceId\" is required."
# remove the leading underscore if provided.
resourceId = resourceId[1:] if resourceId.startswith(
"_") else resourceId
return OslcUtils.recoverResourceId(resourceId)
responseBody = main()

Final Thoughts

In this post we explored how Maximo Integration Object resource Ids are structured, how they can be decoded and how you can construct one using an Maximo Object's primary key column attribute values. These resource Ids are internally generated by Maximo and may change in the future without warning, so we do not suggest you build systems upon the assumption that these Ids will remain in the same format in future versions.

With that said, better understanding how Maximo works and developing your tools to troubleshoot and gather information is always a worthwhile endeavor. We hope you found this post interesting and that it helps you understand Maximo a little better.

If you have any questions, comments or have suggestions for topics you would like to see us cover, please reach out to us at [email protected]

In the time it took you to read this blog post...

You could have deployed Opqo, our game-changing mobile solution for Maximo.

Opqo is simple to acquire, simple to deploy and simple to use, with clear transparent monthly pricing that is flexible to your usage.