It's all about the answers!

Ask a question

How to upload an attachment to a work item via REST API


1
1
Hugh McManus (31112) | asked May 08 '14, 8:31 p.m.
 Hi,

I'm currently working with python to interface with the RTC REST API, and have been having a lot of success so far. I've hit a bit of a problem however in uploading an attachment to a project and linking it to a work item. 

I've done plenty of googling on this one and have found a couple of people using java as a solution however this doesn't really help me. The same article mentions REST but in the form of:

So, it should be doable with HTML (not sure about REST), but it is probably not trivial and undocumented.

I've also had a few people say that they have completed this with OSLC version 2.0 however without any details. 

Currently I'm using the python requests library and the following is my code (assuming authentication has already taken place):

        csrf = ""
        for cookie in self.session.cookies:
            if cookie.name == 'JSESSIONID':
                csrf = cookie.value
        self.session.headers.update({'Content-Type': 'multipart/form-data'})
        post_params = { "projectId": "_At5S5zAUEeKjpdbNKmwlgA",
                   "multiple": "true",
                   "target": "_A_ziQDAUEeKjpdbNKmwlgA",
                   "category": "_BYNbMjAUEeKjpdbNKmwlgA"
        }
        upload_url = "https://hub.jazz.net/ccm/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService/"
        files = {'file': open(file_name, 'rb')}
        post_response = self.session.post(upload_url, params=post_params, files=files)
        print(post_response.json())

With the above I'm getting an error of "invalid post request", not sure what I'm doing wrong but I don't think that I'm that far off.

I've also tried looking at the source of the RQMUtility for uploading attachments without finding out any conclusive differences to what I'm trying to complete here.

Can anyone help?

Cheers,
Hugh

Accepted answer


permanent link
Hugh McManus (31112) | answered May 20 '14, 10:15 p.m.
edited May 20 '14, 10:17 p.m.
 I really don't like answering my own questions but here goes... 

I did manage to get this working in the end up. It's a two step process, firstly uploading the attachment to the project area, and then linking that attachment to the work item.

			def upload_attachment(self, file_location, work_item_number):
		
			        
		
			        # First get the work item details
		
			        filters = {'oslc_cm.properties': 'oslc_cmx:project'}
		
			        attachment_work_item = self.get_work_item(work_item_number, filters=filters)
		
			        if attachment_work_item is None:
		
			            raise Exception("Error: Work item to add link to could not be found")
		
        work_item_project_area = attachment_work_item['oslc_cmx:project']['rdf:resource'].rsplit('/', 1)[1]
        file = open(file_location, 'rb')         file_short_name = os.path.basename(file.name)         files = {'attach': (file_short_name, file,                             'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')}
        # This is really important for requests to work out what kind of content it is         if self.session.headers.__contains__('Content-Type'):             self.session.headers.__delitem__('Content-Type')
        # Not a 100% sure what is the category here???? Seems to be constant for now through         post_params = {"projectId": work_item_project_area,                        "multiple": "true",                        "category": "_IlAckNAwEeOaT8v4QaB0Wg"}         url = self._base_url + "/ccm/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService/"
        upload_response = self.session.post(url, files=files, params=post_params)         if not upload_response.ok:             raise Exception("A problem occurred in retrieving the work item: "                             + upload_response.reason + " - " + upload_response.url)
        return upload_response.json()

This code uses the python requests library. You can then link the attachment to the work item. The key part here is the attachment UUID which is returned from in the above response.

			def add_attachment_link(self, work_item_number, attachment_uuid):
		
        attachment_url = self._base_url + '/ccm/resource/itemOid/com.ibm.team.workitem.Attachment/' + attachment_uuid         attachment_get_repsonse = self._session_get(attachment_url)         attachment_get_repsonse_json = attachment_get_repsonse.json()         attachment_identifier = attachment_get_repsonse_json['dcterms:identifier']         attachment_description = attachment_get_repsonse_json['dcterms:description']
        # May need to add some checking in here to make sure that the work item is not closed or something
        self.session.headers.update({'Content-Type': 'application/json'})
        payload = {'rdf:resource': self._base_url + '/ccm/resource/itemOid/com.ibm.team.workitem.Attachment/'                                    + attachment_uuid,                    'dcterms:title': attachment_identifier.__str__() + ": " + attachment_description,                    }         attachment_collection_url = self._base_url + "/ccm/oslc/workitems/" + work_item_number \                                     + "/rtc_cm:com.ibm.team.workitem.linktype.attachment.attachment"         update_response = self._session_post(attachment_collection_url, payload)
        return update_response.json()


You should then have a link to your workitem.

Ralph Schoon selected this answer as the correct answer

Comments
Wasim Elias commented Nov 12 '14, 4:35 a.m. | edited Jan 19 '16, 10:36 p.m.

Hi ,

I am trying to use you code for similar purpose , but it's not working for me.

can you please show me how did you initialize your session for it to work.

I think I have a problem there , something with authentication ...

Thanks :D

3 other answers



permanent link
Michael A. (15813) | answered Feb 20 '16, 11:17 a.m.
edited Feb 21 '16, 5:31 a.m.
Hi guys,

I am currently working on the same issue. I try to upload an attachment using Java (for RTC v6.0.1), without luck. No matter if I am posting the content directly or using Eclipse Lyo with Fernando's solution, I always see "Invalid POST request". It's not a matter of params as an error in the params section leads to an appropriate error message.

@fernd.fs: Does your solution work with v6 too? If yes it would be cool to know whether you modified your code in some way. It seems as I am missing something here but right now I don't get it.

@hmcmanus could you please post the JSON string you are sending in the body part of the request to the IAttachmentRestService?

Regards
Michael

Comments
1
Donald Nong commented Feb 21 '16, 7:15 p.m.

It's the consensus that there is no published API for uploading attachments to RTC. What most people try to do (and succeed for some) is monitor the traffic in a browser when uploading an attachment, then replicate the same traffic in their own application (regardless the programming language of choice). This results in using the internal API, which may behave differently across versions.


Ralph Schoon commented Feb 22 '16, 3:48 a.m. | edited Feb 22 '16, 4:04 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

The Java API that can be used is explained here:
https://rsjazz.wordpress.com/2012/08/01/uploading-attachments-to-work-items/
https://rsjazz.wordpress.com/2012/09/21/downloading-attachments-from-work-items/

It is the Plain Java API that is provided with RTC for download. I have used that for years now with no changes to the code at all.

This is not a REST based approach. If you want to use a HTTP client, you will have to follow Don's suggestion and basically reverse engineer the protocol.


permanent link
Fernando Silva (8124) | answered Nov 12 '14, 12:37 p.m.
edited Nov 12 '14, 12:40 p.m.
 Hi,

I am using the follwing code to upload files using Lyo Client CM:

Utils.java


private URI uploadAttachment(File file, ChangeRequest request,
JazzFormAuthClient client.String urlRTC, String projectAreaUUID) throws Exception {

URI attachmentUploadUrl = URI
.create(urlRTC
+ "/service/com.ibm.team.workitem.service.internal.rest.IAttachmentRestService/?projectId="+projectAreaUUID+"&multiple=true");
String fileName = file.getName();
byte[] bytes = FileUtils.readFileToByteArray(file);
OutPart outPart = new OutPart();
outPart.setContentType("application/octet-stream; name=" + fileName);
outPart.addHeader("Content-Transfer-Encoding", "binary");
outPart.addHeader("Content-Disposition", "form-data; name=\""
+ fileName + "\"; filename=\"" + fileName + "\"");
outPart.addHeader("Content-Length", String.valueOf(bytes.length));
outPart.addHeader("Accept", "application/text");
outPart.setBody(bytes);
String boundary = "---------------------------"
+ UUID.randomUUID().toString();
BufferedOutMultiPart requestEntity = new BufferedOutMultiPart();
requestEntity.addPart(outPart);
requestEntity.setBoundary(boundary);
ClientResponse response;
synchronized (client) {
response = client.createResource(attachmentUploadUrl.toString(),
requestEntity, "multipart/form-data; boundary=" + boundary);
}
if (response.getStatusCode() != HttpStatus.SC_OK) {
throw new Exception("Failed to upload attachment at "
+ attachmentUploadUrl.toString() + ". "
+ response.getStatusCode() + ": " + response.getMessage());
}
String uploadResponse = response.getEntity(String.class);
JSONObject obj = new JSONObject(uploadResponse.toString().substring(
uploadResponse.indexOf("[") + 1,
uploadResponse.lastIndexOf("]")));
String url = urlRTC
+ "/resource/itemOid/com.ibm.team.workitem.Attachment/"
+ obj.getString("uuid");
return URI.create(url);
}
Client.java
        files=...
	
	
        task=...
	
	
        client=...
	
	
        projectAreaUUID=...
	
	
       urlRTC=...
	
	
	ArrayList<URI> filesURL = new ArrayList<>();

		for (File file : files) {
			URI url = uploadAttachment(file, task, client,urlRTC, projectAreaUUID);
			filesURL.add(url);

		}
		task.getExtendedProperties()
				.put(new QName(RTC_NAMESPACE,
						"com.ibm.team.workitem.linktype.attachment.attachment"),
						filesURL);

	
	

Comments
Russel Alfeche commented Dec 27 '15, 7:39 a.m. | edited Jan 19 '16, 10:37 p.m.

Hi All,
I know this thread is quite old but Im trying to create a straightforward implementation of this using WebClient in c# using your python implementation as pattern, however Im stuck on the part where I need to get the attachment Identifier and description.
See what I have below and let me know what Im missing.
Im stuck at 3.1 since the response Im getting is only the filec contents and not the JSON response containing the attachment ID and description.
Also, when it comes to 3.2 and using POST method, I always get 405: Method not allowed response error.
I also tried PUT but the attachment links are not getting updated.

public void ExecuteMainFlow()
        {
            // 1. Get Project area details from work item number -- WG project area >> _Cr9g8F1AEeGx6_jqJZ63AA
            // Parameter: workItemNumber, Return value: projectArea
            Console.WriteLine("Getting Project Area Detail");
            Console.WriteLine(" PARAMETER: Work Item Number = {0}", MainForm.workItem);
            string jsonResponse = RunGet(@"https://adtclmamr00303.accenture.com/ccm/oslc/workitems/" +
                                            MainForm.workItem + @".json?oslc_cm.properties=rtc_cm:contextId");
            var dict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonResponse);
            string projectArea = dict["rtc_cm:contextId"];
            Console.WriteLine(" RETURN: Project Area = {0}", projectArea);

            // 2. Upload attachment to project area using POST Method

            // Parameter: projectArea, Return value: attItemID (UUID of attachment)
            Console.WriteLine("Uploading attachment to Project Area");
            Console.WriteLine(" PARAMETER: Project Area = {0}", projectArea);
            // string attItemId = RunPost(MainForm.urlPost, MainForm.fileAtt);
            // ***existing attachment hardcoded for testing
            string attItemId = "_b_CQIKvyEeWaxfv-hWy7tA";
            Console.WriteLine(" RETURN: UUID of Attachment = {0}", attItemId);

            // 3.  Add the link to the attachment on the work item      

            Console.WriteLine("Adding attachment link to Work Item");
            // 3.1 Get JSON response from attachment link
            Console.WriteLine(" PARAMETER: UUID of attachment = {0}", attItemId);
            string getResp = RunGet(@"https://adtclmamr00303.accenture.com/ccm/resource/itemOid/com.ibm.team.workitem.Attachment/" +
                                        attItemId);
            // Console.WriteLine(@" RETURN: Attachment ID = {0}, 
                                // Attachment Description = {1}", attId,attDesc);
            // 3.2 Post attachment to Work Item URL
            // Console.WriteLine(@" PARAMETER: Attachment ID = {0}, 
                                // Attachment Description = {1},
                                // UUID of attachment = {2}", attId, attDesc, attItemId);
            // RunFinalPost(MainForm.urlFinalPost, attItemId);
        }


See the URI and Payload used for final post.
URI: https://host/ccm/oslc/workitems/13615.json?oslc_cm.properties=rtc_cm:com.ibm.team.workitem.linktype.attachment.attachment
PAYLOAD: @"{""rdf:resource"": ""https://host/ccm/resource/itemOid/com.ibm.team.workitem.Attachment/_b_CQIKvyEeWaxfv-hWy7tA"",
""oslc_cm:label"": ""9999 : Test Title""
}


permanent link
Ralph Schoon (63.1k33645) | answered May 13 '14, 3:37 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
I am not aware that there is a public API to do this. This might be out of age knowledge. I know that others have used protocol tracking tools to determine how this is done and successfully coded up solutions. It is apparently a series of requests.

I only know how to do this with the Plain Java API. See https://rsjazz.wordpress.com/2012/08/01/uploading-attachments-to-work-items/

Comments
Hugh McManus commented May 13 '14, 6:27 p.m.

Hey Ralph,


Thanks for getting back to me on this one, as my question said I've looked at that wordpress blog. Not much help when you can't use java to be honest.

I did manage to get this working and when I get a chance to clean up the code I'll post it on here.

Cheers,
Hugh 


Ralph Schoon commented May 14 '14, 3:36 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

Hugh,

my blog is Java only so far. That was the intention I had. Maybe I will find time for OSLC etc. some times, but no luck so far.

Once you posted would be great if you could comment on my blog post, so that I can put the link back in there.


Your answer


Register or to post your answer.


Dashboards and work items are no longer publicly available, so some links may be invalid. We now provide similar information through other means. Learn more here.