How to upload an attachment to a work item via REST API
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())
Accepted answer
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.
4 other answers
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
Hey Ralph,
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.
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
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""
}
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
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.
1 vote
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.