Upload attachment using Python SDK v5

Hi,

I’m having a couple issues attaching a pdf to an Asana task, using Python.

Starting with the code from here Upload an attachment

I ended up with the script below. It works if the file link is a web URL e.g. OneDrive (yay!).

First issue, I can’t work out what I’m doing wrong when I try to attach a file from my local hard drive. I’ve tried a few variations of formatting the "file:\" in front of the path but nothing worked.

Second, much smaller issue is Asana doesn’t seem to recognise the attachment as a PDF . It has a generic icon.

I’m new to this. Should I ditch sdk v5 and go to v3 which has more examples?

My script:

import asana
from asana.rest import ApiException
from pprint import pprint

configuration = asana.Configuration()
configuration.access_token = ‘token’
api_client = asana.ApiClient(configuration)

create an instance of the API class

attachments_api_instance = asana.AttachmentsApi(api_client)
opts = {
‘resource_subtype’: “external”, # str |
#‘file’: “Z:\PU06-002 (8.3m x 2m - Triple Roller Drawings).pdf”, # str |
‘parent’: “task gid”, # str |
‘url’: “file:///Z:/CAD%20Database/PDF/100005A.RevB.RW%20Pivot%20Block%20Top_Top.pdf”, # str |
‘name’: “PU06-002.pdf”, # str |
#‘connect_to_app’: True, # bool |
#‘opt_fields’: “connected_to_app,created_at,download_url,host,name,parent,parent.created_by,parent.name,parent.resource_subtype,permanent_url,resource_subtype,size,view_url”, # list[str] | This endpoint returns a compact resource, which excludes some properties by default. To include those optional properties, set this query parameter to a comma-separated list of the properties you wish to include.
}

try:
# Upload an attachment
api_response = attachments_api_instance.create_attachment_for_object(opts)
pprint(api_response)
except ApiException as e:
print(“Exception when calling AttachmentsApi->create_attachment_for_object: %s\n” % e)

This is the response"

Exception when calling AttachmentsApi->create_attachment_for_object: (400)
Reason: Bad Request
HTTP response headers: HTTPHeaderDict({‘Content-Type’: ‘application/json; charset=UTF-8’, ‘Content-Length’: ‘269’, ‘Connection’: ‘keep-alive’, ‘Date’: ‘Thu, 27 Jun 2024 04:49:20 GMT’, ‘Server’: ‘nginx’, ‘cache-control’: ‘no-store’, ‘pragma’: ‘no-cache’, ‘x-frame-options’: ‘DENY’, ‘x-xss-protection’: ‘1; mode=block’, ‘x-content-type-options’: ‘nosniff’, ‘content-security-policy’: “report-uri https://app.asana.com/-/csp_report?report_only=false;default-src ‘none’;frame-src ‘none’;frame-ancestors ‘none’”, ‘x-asana-api-version’: ‘1.0’, ‘asana-change’: ‘name=new_user_task_lists;info=Update on our planned API changes to user task lists (a.k.a. My Tasks), name=new_goal_memberships;info=https://forum.asana.com/t/launched-team-sharing-for-goals/378601;affected=true’, ‘x-robots-tag’: ‘none’, ‘X-Cache’: ‘Error from cloudfront’, ‘Via’: ‘1.1 15f8211e7c4eeeaf8ff1b28a64d08eae.cloudfront.net (CloudFront)’, ‘X-Amz-Cf-Pop’: ‘MEL50-C1’, ‘X-Amz-Cf-Id’: ‘Q_2R8FpAZ6-mkL8qs1xXkPlnJ_UQ74flahcU2N1DrCGNApMEeF6XCw==’, ‘Referrer-Policy’: ‘strict-origin-when-cross-origin’, ‘Strict-Transport-Security’: ‘max-age=31536000; includeSubDomains; preload’, ‘X-UA-Compatible’: ‘IE=edge,chrome=1’})
HTTP response body: b’{“errors”:[{“message”:“url: Invalid URL for link: file:///Z:/CAD%20Database/PDF/100005A.RevB.RW%20Pivot%20Block%20Top_Top.pdf”,“help”:“For more information on API status codes and how to handle them, read the docs on errors: Errors”}]}’

Thanks in advance!

Hi @Caleb_Pearce!

I don’t know Python very well, so I’m going to have to get back to you on that :slight_smile: However, I do know to get correct PDF icon to show up you’ll need to add a specific content-type to the request. In the upload an attachment docs, there’s a note on using curl.

When uploading PDFs with curl, force the content-type to be pdf by appending the content type to the file path: --form "file=@file.pdf;type=application/pdf".

So, I imagine you’ll need to do something to add the type=application/pdf to the upload. When using the JavaScript/Node.js FormData to upload a multi-part form, you would need to do something like:

// fileContent is a readable file stream
const form = new FormData();
form.append( "parent", parentId );
form.append( "file", fileContent, {
  filename: fileName,
  type: "application/pdf"
} );

Hope this helps!

1 Like

Thanks David,

I did get it working! The answer is obvious, once you know - I needed to use resource_subtype = asana for a local file.

The attachment is automatically recognised as a PDF, I didn’t have to add any parameters.

The only change required in the file path is using double backslashes. Sometimes the uplaod works with single backslashes but I get a syntax warning, other times it doesn’t’ work at all.

Working code:

import asana
from asana.rest import ApiException
from pprint import pprint

configuration = asana.Configuration()
configuration.access_token = ‘my_token’
api_client = asana.ApiClient(configuration)

create an instance of the API class

attachments_api_instance = asana.AttachmentsApi(api_client)
opts = {
‘resource_subtype’: “asana”, # str |
‘file’: “C:\Engineering\test.pdf”, # str |
‘parent’: “my_task_gid”, # str |
#‘url’: “”, # str |
#‘name’: “test.pdf”, # str |
#‘connect_to_app’: True, # bool |
#‘opt_fields’: “” #“connected_to_app,created_at,download_url,host,name,parent,parent.created_by,parent.name,parent.resource_subtype,permanent_url,resource_subtype,size,view_url”, # list[str] | This endpoint returns a compact resource, which excludes some properties by default. To include those optional properties, set this query parameter to a comma-separated list of the properties you wish to include.
}

try:
# Upload an attachment
api_response = attachments_api_instance.create_attachment_for_object(opts)
pprint(api_response)
except ApiException as e:
print(“Exception when calling AttachmentsApi->create_attachment_for_object: %s\n” % e)

2 Likes

Thanks, @Caleb_Pearce - as my fellow Forum Leader @Bastien_Siebman likes to say, there is a special place in heaven for those who come back and post working code for others to take advantage of in the future!

2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.