Amit Gujarathi
Giga Sage
Giga Sage

Approval API to Approve / Reject records by authorized user

Hi All,

Hope you are doing fine.

Approval is one of the things which I always used to hate hates when they want to perform from the record view. So in order to ease my self out with the approval thing I have created API for the same code for this is as given below.

 

So as part of approval APIs I have created below resources

  • 2 Rest Resource : For approval and reject

  • 1 Script include : Where we will be defining our entire business logic

 

 /**
         * Description : Approval API to approve the given approval record
	 * Resource Path : /api/swre/sr_approvals/{id}/approve
	 * HTTP method : POST
	 * @ID : SysID of the record to be approved
	 * @comment : Comment to be updtaed on the approval record
	 * @return JSON response
   **/

(function process( /*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

    var Id = request.pathParams.id; // SysID of the approval record
    var bodyParams = request.body.data;
    var comment = bodyParams.comment.toString(); // Comments to be updated on approval record
    return new SRApprovalsAPI(request, response).taskApproved(Id, comment);

})(request, response);

 

 /**
   * Description : Approval API to reject the given approval record
	 * Resource Path : /api/swre/sr_approvals/{id}/reject
	 * HTTP method : POST
	 * @ID : SysID of the record to be rejected
	 * @comment : Comment to be updated on the approval record
	 * @return JSON response
   **/

(function process( /*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {

    var Id = request.pathParams.id;
    var bodyParams = request.body.data;
    var comment = bodyParams.comment.toString();
    return new SRApprovalsAPI(request, response).taskRejected(Id, comment);

})(request, response);

 

/**
 * APIs for Approval/Rejection on sysapproval_approver table
 * 
 * @class 
 * @author Amit Gujarathi
 * @memberof global.module:sys_script_include
 */

var SRApprovalsAPI = Class.create();
SRApprovalsAPI.prototype = {
    initialize: function() {},

    /**
     * Approves the record pending for Approval
     * 
     * @Param {any} sysId Sys ID of the record to be approved
     * @returns {any} 
     */

    taskApproved: function(sysID, comment) {

        if (gs.nil(sysID)) {
            return sn_ws_err.NotFoundError('Please enter a Valid Record');
        }

        if (!this.verifySysId(sysID)) {

            return sn_ws_err.NotFoundError('Please enter a valid record!');

        }

        if (!this.verifyRecordAction(sysID)) {

            var uID = gs.getUserID();
            var recApprover = new GlideRecordSecure("sysapproval_approver");

            recApprover.addQuery('state', 'requested');
            recApprover.addQuery('sys_id', sysID);
            recApprover.addEncodedQuery('approver=' + uID + '^ORapprover.sys_idIN' + this.getUser());
            recApprover.query();
            if (recApprover.next()) {
                recApprover.comments.setJournalEntry(comment);
                recApprover.setValue('state', 'approved');
                recApprover.update();
                return ('Record has been Approved!');
            } else {
                {
                    var serverError = new sn_ws_err.ServiceError();
                    serverError.setStatus(403);
                    serverError.setMessage('You are not authorized to perform this action');
                    return serverError;

                }
            }
        } else {
            var recordActionError = new sn_ws_err.ServiceError();
            recordActionError.setStatus(403);
            recordActionError.setMessage('This record has been actioned previously!');
            return recordActionError;

        }

    },


    /**
     * Gets the users who has appointed the current user as their delegates
     */

    getUser: function() {

        var appr = [];
        var del = new GlideRecord('sys_user_delegate');
        del.addQuery('delegate', gs.getUserID());
        del.addEncodedQuery('approvals=true^ends>=javascript:gs.beginningOfToday()');
        del.query();
        while (del.next()) {
            appr.push(del.user.sys_id);

        }
        return appr.toString();

    },

    /**
     * Verifies if the sys_id a valid in the sysapproval_approver table
     */


    verifySysId: function(sysid) {

        var recApprover = new GlideRecord("sysapproval_approver");
        var result = false;
        if (recApprover.get(sysid)) {
            result = true;
        }
        return result;

    },

    /**
     * Verifies if the record is actioned -  stateINapproved,rejected
     */


    verifyRecordAction: function(sysid) {

        var result = false;
        var recAction = new GlideRecord("sysapproval_approver");
        recAction.addEncodedQuery('state!=requested^sys_id=' + sysid);
        recAction.query();
        if (recAction.next()) {
            result = true;
        }
        return result;

    },

    /**
     * Rejects the record pending for Approval
     * 
     * @Param {any} sysId Sys ID of the record to be approved and comment text for rejection
     * @returns {any} 
     */

    taskRejected: function(sysID, comment) {
        if (gs.nil(sysID)) {
            return sn_ws_err.NotFoundError('Record not Found!');
        }

        if (gs.nil(comment)) {


            var rejectComment = new sn_ws_err.ServiceError();
            rejectComment.setStatus(403);
            rejectComment.setMessage('Rejection comments are mandatory!');
            return rejectComment;



        }

        if (!this.verifySysId(sysID)) {

            return sn_ws_err.BadRequestError('Please enter a valid record!');

        }

        if (!this.verifyRecordAction(sysID)) {
            var uID = gs.getUserID();
            var recApprover = new GlideRecordSecure("sysapproval_approver");


            recApprover.addQuery('state', 'requested');
            recApprover.addQuery('sys_id', sysID);
            recApprover.addEncodedQuery('approver=' + uID + '^ORapprover.sys_idIN' + this.getUser());
            recApprover.query();

            if (recApprover.next()) {

                recApprover.comments.setJournalEntry(comment);
                recApprover.setValue('state', 'rejected');
                recApprover.update();

                return ('Record has been Rejected!');
            } else {

                {
                    var serverError = new sn_ws_err.ServiceError();
                    serverError.setStatus(403);
                    serverError.setMessage('You are not authorized to perform this action');
                    return serverError;

                }
            }

        } else {
            var recordActionError = new sn_ws_err.ServiceError();
            recordActionError.setStatus(403);
            recordActionError.setMessage('This record has been actioned previously!');
            return recordActionError;

        }

    },

    type: 'SRApprovalsAPI'
};

Approval API to Approve / Reject the Approval Record

1. Approve API

  • Request : HTTP Method / URI POST https://.service-now.com/api/sr_approvals//approve

  • Headers : Accept - application/json

  • Content-Type - application/json

  • Request Body : { 'comment' : 'Please approve this record' }

  • Response : Status code : 200 OK

  • Response Body { "result": "Record has been Approved!" }

 

2. Reject API

  • Request : HTTP Method / URI POST https://.service-now.com/api/sr_approvals//reject

  • Headers : Accept - application/json

  • Content-Type - application/json

  • Request Body : { 'comment' : 'Please reject this record' }

  • Response : Status code : 200 OK

  • Response Body { "result": "Record has been Rejected!" }

 

Please be sure to bookmark this article as well as mark it as Helpful if you thought it was helpful.

 

Regards,

Amit Gujarathi

Technomonk Youtube 

Amit Gujarathi Linkedin 

TheTechnomonk.com 

ServiceNow Community Amit Gujarathi 

Comments
TylerTeter
Kilo Guru

Great job putting this together. I had to put something similar together for a custom Slack integration which allowed approvals to be sent from slack actions.

 

Two additional things you might consider adding to your API is to:

1. Impersonate on the users behalf - or allow the API to impersonate and approve on the users behalf. It simplifies the auditability. Also obviously because this API is so powerful, you may want to take extra steps to lock it down.

2. Set the approval source field to something meaningful. It's an absolute pain to track down which approvals are done via API or a special source. You can easily set up an approval source choice, and set it via the API.

Amit Gujarathi
Giga Sage
Giga Sage

Thanks a lot @TylerTeter this is really helpful

Jaipal Patlolla
Tera Explorer

Do we know if this is compliant to the ServiceNow licensing?

Amit Gujarathi
Giga Sage
Giga Sage

It doesn't required any licensing as its not a part of integration hub

Version history
Last update:
‎10-19-2022 08:30 PM
Updated by:
Contributors