Posting a feedback file to a dropbox via API

Sean.R.636
Sean.R.636 Publicaciones: 4 🌱

I have been trying to write code either via

POST /d2l/api/le/(version)/(orgUnitId)/dropbox/folders/(folderId)/feedback/(entityType)/(entityId)/attach or

POST /d2l/api/le/(version)/(orgUnitId)/dropbox/folders/(folderId)/feedback/(entityType)/(entityId)/upload

I've been trying to upload a specific small file as a test, and have been receiving a 400, 404, or 416 error when trying to upload. In contrast, I have been able to use my Python script to post grades, dropbox score, and text/html feedback. Would you be able to provide some pointers as to the form of the payload required? Also, my admin has enabled the current scopes:

SCOPE = "core:*.* : dropbox:access:read dropbox:folders:read,write enrollment:orgunit:read grades:access:read grades:gradecategories:read grades:gradeobjects:read grades:gradevalues:read,write quizzing:access:read quizzing:attempts:read quizzing:quizzes:read managefiles:*.*"

The function call below this post is what I attempted for file uploading in Python (spacing messed up in cut/paste). Do you have any suggestions? Thanks in advance for any help you can provide.


def attach_feedback_file_prototype(access_token, org_unit_id, folder_id, user_id, file_path):
"""
Attaches a feedback file by POSTing the raw file data.This function implements:
POST /d2l/api/le/(version)/(orgUnitId)/dropbox/folders/(folderId)/feedback/(entityType)/(entityId)/attach?filename=...
"""

print(f"Preparing to attach file '{file_path}' to User ID {user_id}...")

# 1. Get file details
if not os.path.exists(file_path):
print(f"❌ ERROR: File not found: {file_path}")
return

file_name = os.path.basename(file_path)
mime_type, _ = mimetypes.guess_type(file_path)
if mime_type is None:
mime_type = 'application/octet-stream' # Default

entity_type = "user"

# 2. Build API URL, Headers, and Params
api_url = (
f"{API_BASE_URL}/d2l/api/le/{API_VERSION}/{org_unit_id}/dropbox/folders/"
f"{folder_id}/feedback/{entity_type}/{user_id}/attach"
)

# We pass the filename as a URL parameter
params = {
'filename': file_name
}

headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': mime_type # Set content-type to the file's mime type
}

# 3. Read the raw file data
try:
with open(file_path, 'rb') as f:
file_data = f.read()

# 4. Print the POST call to the console
print("\n--- Printing POST Call Details ---")
print(f"METHOD: POST")
print(f"URL: {api_url}")
print(f"PARAMS: {json.dumps(params, indent=4)}")
print(f"HEADERS:")
print(json.dumps(headers, indent=4))
print(f"BODY: (Raw bytes of {file_name}, {len(file_data)} bytes)")
print("------------------------------------")

# 5. Attempt the posting
print("\nAttempting to POST raw file data...")

# We use the 'data' param to send the raw bytes
response = requests.post(
api_url,
headers=headers,
params=params,
data=file_data
)

response.raise_for_status()

print(f"✅ SUCCESS: {response.status_code} {response.reason}")
print("File attached successfully.")

response_data = response.json()
print("\n--- Server Response Data ---")
print(json.dumps(response_data, indent=2))
print("------------------------------")

except requests.exceptions.HTTPError as err:
print(f"❌ API CALL FAILED: {err.response.status_code} - {err.response.reason}")
print("--- SERVER ERROR RESPONSE ---")
print(err.response.text)
print("-----------------------------")
except IOError as e:
print(f"❌ File Error: Could not read file {file_path}. {e}")
except Exception as e:
print(f"❌ An unexpected error occurred: {e}")

Comentarios

  • Joshua.G.520
    Joshua.G.520 Publicaciones: 9 🌱

    Hi Sean,

    I haven't used those specific calls before but they look like the same style as other file upload API calls that I have used, e.g., to upload and save a file to manage files. They need first the /upload call to upload the file, which returns a file key. Then follow that with a /save call, using the key from the /upload, to save the file. At a quick glance your /upload and /attach calls look like a similar structure, but I don't see the /upload step in your code. I think you'll need that. Hope this helps!

    Josh.