Join 34,000+ subscribers and receive articles from our blog about software quality, testing, QA and security.
 

[API] Error when trying to add attachment using add_attachment_to_result

Hi,

with TestRail 5.7 some new routes have been added to support uploading of attachments.

Following the documentation (http://docs.gurock.com/testrail-api2/reference-attachments) I am trying to upload a scrrenshot to a test result like this:

Dictionary<string, object> returnData = new Dictionary<string, object>();

returnData.Add(“content-type”, “multipart/form-data”);
returnData.Add(“attachment”, screenShotPath);

m_TestRailApiClient.SendPost(“add_attachment_to_result/” + resultId, returnData);

However, the API returns always the following error:

“Maximum POST or upload size was exceeded.”

The screenshot itself is only 100kB in size, when uploading it manually there is no such error. Is there any setting that I am missing?

Best regards,
Christian

I don’t know what library you are using, but it seems you put the attachment in body instead of header?
Maybe try to hit using cUrl first and see if it works for your file?

I am using the C# library provided by Gurock (http://docs.gurock.com/testrail-api2/bindings-dotnet).

After your post I now changed to a plain HttpWebRequest and set the ContentType and Headers like you suggested, but the returned error stays the same.

I am having issues as well. It appears that the “Maximum POST or upload size was exceeded.” error message is a generic error message that is sent when there isn’t a more specific error message and often comes from malformed body or query. (this is according to an old post from an employee at TestRail). @gavril as you seem to have this working, can you give us an example of a call that works for you? If you have it working via code, that would be great as well.

Are there any restrictions on the file type of the attachment? And do we just put the file path in as the value for “Attachment”, or does it need to be the file bytes or something else?

Thank you!

Hi @MysteryPerson, @ChristianDiedrich,

We are working to update our bindings and related documentation for the new API endpoints, as the structure of a POST attachment request is considerably different from the other methods.

In the meantime, you may be able to use the following code for the SendRequest function:

private object SendRequest(string method, string uri, object data)

{
string url = this.m_url + uri;

		// Create the request object and set the required HTTP method
		// (GET/POST) and headers (content type and basic auth).
		HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
		request.ContentType = "application/json";
		request.Method = method;

		string auth = Convert.ToBase64String(
			Encoding.ASCII.GetBytes(
				String.Format(
					"{0}:{1}",
					this.m_user,
					this.m_password
				)
			)
		);

		request.Headers.Add("Authorization", "Basic " + auth);

		if (method == "POST")
		{
            if (uri.StartsWith("add_attachment"))
            {
                string boundary = String.Format("{0:N}", Guid.NewGuid());
                string filePath = (String)data;

                // Set the http request header \\
                request.ContentType = "multipart/form-data; boundary=" + boundary;

                // Form the post data request
                MemoryStream postDataStream = new MemoryStream();
                StreamWriter postDataWriter = new StreamWriter(postDataStream);

                // Include the file details in the post data
                postDataWriter.Write("\r\n--" + boundary + "\r\n");
                postDataWriter.Write("Content-Disposition: form-data; name=\"attachment\";"
                                + "filename=\"{0}\""
                                + "\r\nContent-Type: {1}\r\n\r\n",
                                Path.GetFileName(filePath),
                                Path.GetExtension(filePath));
                postDataWriter.Flush();

                // Read the file
                FileStream attachmentStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = attachmentStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    postDataStream.Write(buffer, 0, bytesRead);
                }
                attachmentStream.Close();

                postDataWriter.Write("\r\n--" + boundary + "--\r\n");
                postDataWriter.Flush();

                // Set the http request body content length
                request.ContentLength = postDataStream.Length;

                // Dump the post data from the memory stream to the request stream
                using (Stream requestStream = request.GetRequestStream())
                {
                    postDataStream.WriteTo(requestStream);
                }
                postDataStream.Close();
            }
            // For non-attachment requests, add the POST arguments, if any. 
            // We just serialize the passed data object (i.e. a dictionary)
            // and then add it to the request body.
            else if (data != null)
            {
                byte[] block = Encoding.UTF8.GetBytes(
                    JsonConvert.SerializeObject(data)
                );
                request.GetRequestStream().Write(block, 0, block.Length);
            }
		}

		// Execute the actual web request (GET or POST) and record any
		// occurred errors.
		Exception ex = null;
		HttpWebResponse response = null;
		try
		{
			response = (HttpWebResponse)request.GetResponse();
		}
		catch (WebException e)
		{
			if (e.Response == null)
			{
				throw;
			}

			response = (HttpWebResponse)e.Response;
			ex = e;
		}

		// Read the response body, if any, and deserialize it from JSON.
		string text = "";
		if (response != null)
		{
			var reader = new StreamReader(
				response.GetResponseStream(),
				Encoding.UTF8
			);

			using (reader)
			{
				text = reader.ReadToEnd();
			}
		}

		JContainer result;
		if (text != "")
		{
			if (text.StartsWith("["))
			{
				result = JArray.Parse(text);
			}
			else 
			{
                try
                {
                    result = JObject.Parse(text);
                }
                catch   // Response is not in JSON format
                {
                    throw new APIException(String.Format(
                        "TestRail API returned the following: {0}\n",
                        text));
                }
			}
		}
		else 
		{
			result = new JObject();
		}

		// Check for any occurred errors and add additional details to
		// the exception message, if any (e.g. the error message returned
		// by TestRail).
		if (ex != null)
		{
			string error = (string) result["error"];
			if (error != null)
			{
				error = '"' + error + '"';
			}
			else
			{
				error = "No additional error message received";
			}

			throw new APIException(
				String.Format(
					"TestRail API returned HTTP {0} ({1})",
					(int)response.StatusCode,
					error
				)
			);
		}

		return result;
	}
}

This code should allow you to submit an attachment to TestRail’s API with the following format:
JObject c = (JObject)client.SendPost("add_attachment_to_result/1", "C:\\Downloads\\file.txt");

This code should work as expected, by submitting the path to your attachment as the post body. The official binding files may end up being slightly different when released.

Hope this helps,
Jon

3 Likes

Your suggestion worked well, thank you! One thing I did notice is that after I made 2 calls to attach images, a third call would timeout. I was stepping through the code and it was definitely not making too many calls per second (I’ve run across that before and this is different). Is there a different restriction involved with attaching images? Is it limited to 2 calls per minute or something? I have tried even adding over 1 min wait between calls to add an attachment, but that still times out on the 3rd call.

Hi @MysteryPerson,

Glad to hear the bindings helped! I tested the bindings and uploaded 10 or 12 attachments in a minute or two without issue.

A timeout issue would likely be network related as there wouldn’t be a preset or different rate limit for attachment calls. If you did hit a rate limit with TestRail’s API, you should receive a separate 429 response code for this.

When the request times out, are you able to make ‘GET’ requests in the meantime, or do all requests timeout?

Regards,
Jon

@jon.reynolds I dug into the issue further and tried a few different things with little to no success. It looks like I can still make ‘GET’ requests via the UI while attempting to upload attachments, but I don’t think it is a network issue. For reference, I am using C# in Visual Studio Enterprise. I was attempting to upload 3 attachments for 3 different tests one after the next. If I ran the code in debugger mode (without breakpoints), the 3 attachments would be added successfully every single time. When I ran it without debugger, it would always timeout when adding the 3rd attachment. I ran it both ways many times and had the same result each time. I haven’t been able to get to the actual values as how I have it running, there isn’t much of a way to get any data out (and again, when I ran it in debug, it would never fail). Do you have any ideas/thoughts on what to try next or where to go?

Hi @MysteryPerson,

I’d be glad to help troubleshoot this further with you. Would you mind emailing us at contact@gurock.com with additional details about the attachments you are uploading and the URL of your TestRail instance? If the attachments do not include sensitive data, can you send those to us as well?

Thanks,
Jon

Hi @jon.reynolds ,
I am using a similar code but in Java,
and I am getting the same issue : “error":"Maximum POST or upload size was exceeded.”

code :
Map<String, Comparable> data = new HashMap<String, Comparable>();
data.put(“content-type”, “multipart/form-data”);
data.put(“attachment”, “Screenshots/screenhotfile.png”);

Object res = client.sendPost(“add_attachment_to_result/” + resultId, data);

Would you have a suggestion to make screenshot upload to TestRail work ?
Thank you

to complete/resolve this post: my colleague was sent this reply by email by Jon :

Thanks for the email! We do have updated Java API bindings available for download and these have been used by customers without issue. You can find these and example attachment requests on our website here:

http://docs.gurock.com/testrail-api2/bindings-java

==> tested (by using the new file APIClient.java), and it was working !!!
Thank you for this

1 more question :
what change would it require, if I wanted to upload the picture so that it is seen as a small inline thumbnail (like when we manually attach an image to the comment text field in TR), instead of as an attached file (with no preview) ?

Regards