Update:
This is an older version of the question/script. The new version can be found here: Part 2: Create or update record via HTTP request
I have an external system that sends an HTTP request to a Jython script (in IBM's Maximo Asset Management platform).
The Jython 2.7.0 script does this:
- Accepts an HTTP request:
http://server:port/maximo/oslc/script/CREATEWO?_lid=wilson&_lpwd=wilson&f_wonum=LWO0382&f_description=LEGACY WO&f_classstructureid=1666&f_status=APPR&f_wopriority=1&f_assetnum=LA1234&f_worktype=CM
- Loops through parameters:
- Searches for parameters that are prefixed with
f_
('f' is for field-value) - Puts the parameters in a list
- Removes the prefix from the list values (so that the parameter names match the database field names).
- Searches for parameters that are prefixed with
- Updates or creates records via the parameters in the list:
- If there is an existing record in the system with the same work order number, then the script updates the exiting record with the parameter values from the list.
- If there isn't an existing record, then a new record is created (again, from the parameter values from the list).
- Finishes by returning a message to the external system (message: updated, created, or other (aka an error)).
Can the script be improved?
from psdi.mbo import SqlFormat
from psdi.server import MXServer
from psdi.mbo import MboSet
params = list( param for param in request.getQueryParams() if param.startswith('f_') )
paramdict={}
resp=''
for p in params:
paramdict[p[2:]]=request.getQueryParam(p)
woset = MXServer.getMXServer().getMboSet("workorder",request.getUserInfo())
#Prevents SQL injection
sqf = SqlFormat("wonum=:1")
sqf.setObject(1,"WORKORDER","WONUM",request.getQueryParam("f_wonum"))
woset.setWhere(sqf.format())
woset.reset()
woMbo = woset.moveFirst()
if woMbo is not None:
for k,v in paramdict.items():
woMbo.setValue(k,v,2L)
resp = 'Updated workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()
else:
woMbo=woset.add()
for k,v in paramdict.items():
woMbo.setValue(k,v,2L)
resp = 'Created workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()
responseBody = resp
Note 1: Previously, there was an SQL injection vulnerability in the code. This issue has been resolved via the SQLFormat java class. The code has been updated.
Note 2: Unfortunately, I'm not able to import Python 2.7.0 libraries into my Jython implementation. In fact, I don't even have access to all of the standard python libraries.
Note 3: The acronym 'MBO' stands for 'Master Business Object' (it's an IBM thing). For the purpose of this question, a Master Business Object can be thought of as a work order record. Additionally, the constant 2L tells the system to override any MBO rules/constraints.
1 Answer 1
Can the script be improved?
Jython is somewhat of a horror show, so improvements are limited but still possible.
List comprehensions
params = list( param for param in request.getQueryParams() if param.startswith('f_') )
can be
params = [
param for param in request.getQueryParams()
if param.startswith('f_')
]
Dict comprehensions
This just squeaked into Python 2.7:
paramdict = {
p[2:]: request.getQueryParam(p)
for p in params
}
Factor out common code
Factor out common code from these blocks:
if woMbo is not None:
for k,v in paramdict.items():
woMbo.setValue(k,v,2L)
resp = 'Updated workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()
else:
woMbo=woset.add()
for k,v in paramdict.items():
woMbo.setValue(k,v,2L)
resp = 'Created workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()
For example,
if woMbo is None:
woMbo=woset.add()
verb = 'Created'
else:
verb = 'Updated'
for k,v in paramdict.items():
woMbo.setValue(k,v,2L)
resp = verb + ' workorder ' + request.getQueryParam("f_wonum")
woset.save()
woset.clear()
woset.close()
Guaranteed closure
Wrap your code in a try
/finally
:
woset = MXServer.getMXServer().getMboSet("workorder",request.getUserInfo())
try:
# ...
finally:
woset.close()
Named constants
the constant
2L
tells the system to override any MBO rules/constraints
Fine; so it should get first-class treatment:
IGNORE_RULES = 2L
f_wonum
isNumeric()
will prevent the SQL attacks for this script, assuming thatf_wonum
is a number. \$\endgroup\$f_wonum
is not a number. What I've done is utilized the SQLFormat Java class for this. I've updated Note 1 in the question. Cheers! \$\endgroup\$