What does the PUT /d2l/api/le/(version)/(orgUnitId)/content/topics/(topicId)/file want? If a...
What does the PUT /d2l/api/le/(version)/(orgUnitId)/content/topics/(topicId)/file want?
If a working example is available it would be very help, but I currently have no idea what to actually provide this route.
I am building an app using react and the "superagent-d2l-session-auth" npm library
the route method looks like this (feel free to take it)
----- (Typescript)
callPutRoute = (apiData:any, orgUnitId:string, topicId:string, fileString:string) => {
return new Promise((resolve, reject) => {
var request = require('superagent'),
auth = require('superagent-d2l-session-auth')();
let route = this.routeTemplate.replace("[API_VER]", apiData.LE_version)
route = route.replace("[ORG_UNIT_ID]", orgUnitId)
route = route.replace("[TOPIC_ID]", topicId)
let reqData = fileString // lets assume this is a bunch of html
// is it like widget data where I need this format "{Data:fileString}"?
// do I use on the the templates here? https://docs.valence.desire2learn.com/basic/fileupload.html#simple-uploads
request
.put(route)
.use(auth)
.set('Content-Type', 'multipart/mixed;boundary=xxBOUNDARYxx') // is this required?
.send(reqData)
.end((err:any, response:any) => {
if (err !== null) {
console.error("PUT replaceTopicFile call failed");
console.log(err);
return reject(err);
}
console.log("PUT replaceTopicFile call success");
console.log(response.body);
resolve(response.body);
});
});
}
---------
Answers
-
This route uses the simple file upload process (https://docs.valence.desire2learn.com/basic/fileupload.html#simple-uploads); there are a number of examples in the docs in the linked topic above and while none of them exactly use the API route you indicate, they can all act as a guide for what the request should look like.
A good example of providing a binary file data to Brightspace this way is the "updating the profile image" example. In the Content-Disposition part header, the API you're indicating wants you to provide "file" for the value of the name parameter (where the profile image route uses the value "profileImage").
Note that the simple file upload pattern does not use an overall "multipart/mixed" payload; it uses the "multipart/form-data" content type.
-
perhaps I need more details in this question. I've tried several of the templates mentioned in the documentation.
what I am usually getting is a 400 error indicating I am missing a detail in the request.
(side note, the error is linking me here http://docs.valence.desire2learn.com/res/apiprop.html#invalid-parameters, where the "invalid-parameters" anchor doesn't seem exist)
Here's a full example of the request as it appear in chrome.
--- REQUEST HEADER DATA ---
:method: PUT
:path: /d2l/api/le/1.41/308299/content/topics/12025662/file
[.....]
content-length: 124
content-type: multipart/form-data; boundary=xxBOUNDARYxx
[.....]
x-csrf-token: (let's just assume this is right)
---- PAYLOAD ----
--xxBOUNDARYxx
Content-Disposition: form-data; name='file'
Content-Type: text/html
this is some html
--xxBOUNDARYxx--
---- RESPONSE ----
{
"type":"http://docs.valence.desire2learn.com/res/apiprop.html#invalid-parameters",
"title":"Invalid Parameters",
"status":400,
"detail":"Request has missing or invalid parameters."
}
-
Hmmm. It's possible that your part content disposition header also requires a "filename" parameter, which you're not providing, but I'm unsure that's the problem.
-
Below is a working example of a PowerShell snippet I use to upload files.
param ( $instance, $token, $Course, $FilePath )
$fileName = Split-Path -Path $($FilePath) -Leaf
$fileBin = [IO.File]::ReadAllBytes($FilePath)
$enc = [System.Text.Encoding]::GetEncoding("iso-8859-1")
$fileEnc = $enc.GetString($fileBin)
$LF = "`r`n"
$Pbody = (
"Content-Type: multipart/form-data; boundary=xxBOUNDARYxx",
"Content-Length: $($fileEnc.Length)$LF",
"--xxBOUNDARYxx",
"Content-Disposition: form-data; name=`"Image`"; filename=`"$fileName`"",
"Content-Type: application/octet-stream$LF",
$fileEnc,
"--xxBOUNDARYxx--$LF"
) -join $LF
$header = @{Accept = "*/*"; 'Content-Type' = "multipart/form-data; boundary=xxBOUNDARYxx"; Authorization = "Bearer $($token.access_token)"}
# Note: File is uploaded to "Course Offering Files" location
$endPoint = "/courses/$($Course.Identifier)/image"
$uri = "https://" + $instance.uri + "/d2l/api/lp/" + $API_ver_lp + $endPoint
Invoke-RestMethod -Method PUT -Uri $uri -Headers $header -Body $Pbody
Hope this helps,
Good luck
-
Hello,
When I originally changed header to "multipart/form-data" as Viktor suggested, I had apparently also tossed a ";" after the boundary causing the 400 errors.
for posterity this worked (at least for txt and html)
var xhr = new XMLHttpRequest();
xhr.open("PUT", route, true);
xhr.setRequestHeader("X-Csrf-Token", localStorage["XSRF.Token"]);
xhr.setRequestHeader(
"Content-Type",
"multipart/form-data;boundary=xxBOUNDARYxx"
);
var body = `--xxBOUNDARYxx`;
body += `Content-Disposition:form-data;name="file";filename="${fileName}"`;
body += `Content-Type:text/html; charset="UTF-8"`;
body += `${htmlBody}`;
body += `--xxBOUNDARYxx--`;
Thanks for you help.
-
Excellent!