Send attachments as part of REST message

motto
Kilo Expert

*** SKIP TO THE BOTTOM OF THIS THREAD IF YOU WANT THE CODE*** 
USE CASE: UI Action that is used to subjectively escalate an incident to another ServiceNow instance - Incident created on other instance will have relevant metadata and attachments. 

Hi Community 

I have completed the initial steps I need to integrate two instances together. Effectively, I have a UI Action for the incident form, that on click, will duplicate the incident record to the opposing instance as part of an escalation. 

Stating that, attachments are not being transferred at this moment and time as the record is created on the opposing instance. I am looking for a simple way to transfer any attachments that may exist as part of the initial REST call, and came across the setRequestBodyFromAttachment method. Attempted to apply it based on the following article: 

https://community.servicenow.com/community?id=community_question&sys_id=4b3ac3a9db5cdbc01dcaf3231f96...

Here is the code:

(function () {
	try {
		var body = {
			short_description: current.getValue('short_description'),
			description: current.getValue('description'),
			// caller_id: current.getValue('caller_id'),
			incident_number: current.getValue('number'),
			u_lyrical_incident: current.getValue('number'),
			//correlation_id: current.getValue('number'),
			assignment_group: "Operations",
			correlation_id: current.getValue('sys_id'),
			cmdb_ci: current.getDisplayValue('u_target_system'),
			// cmdb_ci: current[u_target_system].getLabel(),
			comments: current.comments.getJournalEntry(-1),
		};
		var tosend = JSON.stringify(body);
		var request = new sn_ws.RESTMessageV2('Ottos test instance', 'Otto_Post');
		request.setRequestHeader("Accept", "application/json");
		request.setRequestHeader('Content-Type', 'application/json');
		request.setRequestBody(tosend);
		request.setRequestBodyFromAttachment(current.getValue('sys_id'));
		request.setWorkflow (false); //attempt to stop loops
		var response = request.execute();
		var responseBody = response.getBody();
		var json = responseBody;
		var obj = JSON.parse(json); //define JSON parsing for the response JSON file to decode
		var foundSysID = obj.result.sys_id; //decoded newly created incident sys_id on opposing instance
		var foundNumber = obj.result.number;
		current.correlation_id = foundSysID; //copied the sys_id incident to correlation id
		current.u_customer_ticket = foundNumber;
		current.u_escalated = 1;
		current.update(); //update the current incident
		gs.addInfoMessage("This incident has been escalated to opposing instance :  " + foundNumber); //log ticket number
		gs.log (tosend);
		gs.log (foundnumber);
		var httpStatus = response.getStatusCode();
	} catch (ex) {
		var message = ex.message;
	}
})();

This fails with the following error:

REST Msg Outbound - RESTMessageClient : Error executing REST request: attachment does not exist with sys_id: eea0c283db851c10965e303f9d961919: com.glide.rest.util.RESTRuntimeException: attachment does not exist with sys_id: eea0c283db851c10965e303f9d961919:

Obviously I am not using this correctly or mispercieve how its going to be used. 

1) What is the best method to send an attachment? 

2) If this is the best method, I am clearly not understanding, and am very receptive to being corrected

Thank you for your help

 

1 ACCEPTED SOLUTION

Michael Ritchie
ServiceNow Employee
ServiceNow Employee

Back at Knowledge 17 in CreatorCon we had "Hack Labs" which were short labs to learn various concepts and the REST Attachment API was one of those.  I wrote this lab that covers your use case of integrating two instances and sending the attachments along with it.  See the later labs in the attachment document.

View solution in original post

9 REPLIES 9

Mike Patel
Tera Sage

Try using below. I think you need to pass sys_id of attachment not the ticket. Based on script looks like you are running this on incident table.

(function() {
	try {
		var body = {
			short_description: current.getValue('short_description'),
			description: current.getValue('description'),
			// caller_id: current.getValue('caller_id'),
			incident_number: current.getValue('number'),
			u_lyrical_incident: current.getValue('number'),
			//correlation_id: current.getValue('number'),
			assignment_group: "Operations",
			correlation_id: current.getValue('sys_id'),
			cmdb_ci: current.getDisplayValue('u_target_system'),
			// cmdb_ci: current[u_target_system].getLabel(),
			comments: current.comments.getJournalEntry(-1),
		};
		var attachmentid = '';
		var gr = new GlideRecord("sys_attachment");
		gr.addQuery("table_sys_id", current.getValue('sys_id'));
		gr.query();
		if (gr.next()) {
			attachmentid = gr.getValue('sys_id');
		}

		var tosend = JSON.stringify(body);
		var request = new sn_ws.RESTMessageV2('Ottos test instance', 'Otto_Post');
		request.setRequestHeader("Accept", "application/json");
		request.setRequestHeader('Content-Type', 'application/json');
		request.setRequestBody(tosend);
		request.setRequestBodyFromAttachment(attachmentid);
		request.setWorkflow(false); //attempt to stop loops
		var response = request.execute();
		var responseBody = response.getBody();
		var json = responseBody;
		var obj = JSON.parse(json); //define JSON parsing for the response JSON file to decode
		var foundSysID = obj.result.sys_id; //decoded newly created incident sys_id on opposing instance
		var foundNumber = obj.result.number;
		current.correlation_id = foundSysID; //copied the sys_id incident to correlation id
		current.u_customer_ticket = foundNumber;
		current.u_escalated = 1;
		current.update(); //update the current incident
		gs.addInfoMessage("This incident has been escalated to opposing instance :  " + foundNumber); //log ticket number
		gs.log(tosend);
		gs.log(foundnumber);
		var httpStatus = response.getStatusCode();
	} catch (ex) {
		var message = ex.message;
	}
})();

Thank you again

The code, as provided results in a coaching loop:

Any idea on cause?

motto
Kilo Expert

Thank you for your swift reply 

You were correct - I am testing at the moment, and will reply as soon as I have a result

 

MrMuhammad
Giga Sage

Hi,

In servicenow, attachments are stored in dedicated/separate table <sys_attachment> with sys_id of associated record and table name. So, You need to hit the <sys_attachment> table and passing sys_id of the corresponding record and table on the target instance. Here is the code I have in place for sending attachments across the instances. 

sendAttachments : function(sourceTable, sourceID, targetID) {

//sourceTable - Incident
//sourceID - source incident sysid
//targetID - target record sys_id
//u_attached - custom field to keep track of alread sent attachments.

			var answer = [0, 0]; //successful attachments, failed attachments
			
			// Query for any attachments on the current record.
			var attachmentRec = new GlideRecord("sys_attachment");
			attachmentRec.addEncodedQuery('u_attached=false^table_name=incident^table_sys_id='+sourceID);
			attachmentRec.query();
			gs.log("--> "+attachmentRec.getRowCount());
			if (attachmentRec.hasNext()) {
				while (attachmentRec.next()) {
					var attachmentMessage = new sn_ws.RESTMessageV2();
					attachmentMessage.setHttpMethod("post");
					attachmentMessage.setAuthenticationProfile('basic', <sys_id_basic_credentials>);
					attachmentMessage.setEndpoint(gs.getProperty(<base_url>) + "api/now/attachment/file");
					attachmentMessage.setQueryParameter("table_name", attachmentRec.table_name);
					attachmentMessage.setQueryParameter("table_sys_id", targetID);
					attachmentMessage.setQueryParameter("file_name", attachmentRec.file_name);
					attachmentMessage.setRequestHeader("Content-Type", attachmentRec.content_type);
					attachmentMessage.setRequestHeader("Accept", "application/json");
					attachmentMessage.setRequestBodyFromAttachment(attachmentRec.sys_id); // send the attachment as request Body
					
					var response = attachmentMessage.execute();
					var responseBody = response.getBody();
					
					var httpStatus = response.getStatusCode();
					gs.log("=== "+ httpStatus);
					if (httpStatus.toString() == "201") {
						
						answer[0] += 1;
						attachmentRec.u_attached = 'true';
						attachmentRec.update();
						
					} else {
						answer[1] += 1;
					}
				}
			} else {
				answer = "none";
			}
			return answer;
		},
		

  

I hope this would be of help. 

 

Please mark this accepted/helpful, if applicable.

 

Thanks & Regards,

Sharjeel

Regards,
Muhammad