July 24, 2023
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.
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 | Replacement |
---|---|
null | ~NULL~ |
boolean | 0 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.
Value | Replacement |
---|---|
= | - |
+ | _ |
/ | ~ |
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
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.
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
.
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.
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...}
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.
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.
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();}}}
from com.ibm.tivoli.maximo.oslc.provider import LocalURIGenerator, OslcResourceCachefrom psdi.mbo import SqlFormatfrom psdi.server import MXServerdef 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()
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.
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);}}
from com.ibm.tivoli.maximo.oslc import OslcUtilsdef 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 resourceIdreturn OslcUtils.recoverResourceId(resourceId)responseBody = main()
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]