Microsoft Graph API unable to update Excel file - excel

I have an Excel file (.xlsx), which when I have it already in my OneDrive, I can use a REST command like this, to modify it:
/v1.0/me/drive/root:/SpreadSheetName.xlsx:/workbook/worksheets/content_stats/tables('RawStats')/Rows
When I modify my code to first upload the file to OneDrive (rather than using the file that is already there), and I use the REST API, I get the error:
Open navigation properties are not supported on OpenTypes. Property name: 'tables'.
I have searched the web for this message, and cannot find anything related to what I am doing. The REST call for modifying the file which was just uploaded is nearly identical, although I do reference the file by ID instead, as that is what is returned by the upload API. This is the URL I use to modify the file which was just uploaded.
/v1.0/me/drive/items:/<RealExcelSpreadsheetID>:/workbook/worksheets/content_stats/tables('RawStats')/Rows
Both are doing a POST. Exact same file, only difference is it is being uploaded first, rather than already being in OneDrive. The file was definitely uploaded correctly, as when I go through the OneDrive web interface I do find it and can view it online. This is a business account.
It was uploaded as MIME type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.
It uses these scopes delegated via OAuth2:
User.Read
User.ReadWrite
Files.Read
Files.ReadWrite
Files.ReadWrite.All
Sites.ReadWrite.All
Using Node.js and JavaScript, although that should't matter.
Here is the code used to upload the file:
function copyTemplateInOneDrive(res, queryParam, officeAccessToken, callback) {
var fs = require('fs');
var excelExt = ".xlsx";
var excelSpreadsheetFilenameStart = "stats";
var uploadUrl = "https://graph.microsoft.com/v1.0/me/drive/root:/" +
excelSpreadsheetFilename + dateNowFull() + excelExt + ":/content";
var xlsxMimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
fs.readFile(excelSpreadsheetTemplateFilename, function read(error, fileContent) {
var request = require('request');
var options = {
url: uploadUrl,
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + officeAccessToken,
'Content-Type': xlsxMimeType,
},
body: fileContent
};
request.put(options, function (error, response, body) {
var result = JSON.parse(body);
if ('id' in result) {
console.log("Successfully uploaded template file to OneDrive");
res.write("Successfully uploaded template file to OneDrive");
var excelSpreadsheetID = result.id;
excelSpreadsheetUrl = result.webUrl;
} else {
console.log("ERROR: unable to upload template file to OneDrive " + body);
res.write("Error: unable to upload template file to OneDrive" + body);
return;
}
callback(null, res, queryParam, officeAccessToken, excelSpreadsheetID);
});
});
}
It uses the async module from node.js (which makes use of callback). It also saves the ID returned, and later passes it into the call to the Microsoft Graph.

Your issue here is the URL you're calling with the id is using syntax expecting the file path (i.e. folder\filename.ext) rather than the id. This is why switching to the file name started working for you.
There are two ways to address a file stored in OneDrive:
drive/items/{item-id}
/drive/root:/path/to/file (note the :)
You correctly switched your URI from drive/root to drive/items but by leaving the : in place you are telling OneDrive to address the file by it's path rather than it's id. In other words, it's looking for a file named "{some-id}".
For addressing a file by it's path, your URL is correct:
/drive/root:/{file-path}:/workbook/worksheets/content_stats/tables('RawStats')/Rows
For addressing a file by it's id however, you need to drop the ::
/drive/items/{file-id}/workbook/worksheets/content_stats/tables('RawStats')/Rows
You can read about how files are addressed in the documentation for DriveItem.

Related

Why is my binary file in OneDrive, sent first from Postman then forwarded by Node.js code using Graph API, not identical to the original?

I use Postman to POST a binary (Excel) file to my Node.js (Typescript) Azure function, which PUTs the body of the request on to Microsoft Graph API as a new file upload - so it acts as a proxy for an upload to OneDrive using the Graph API. I use axios rather than the Graph client.
The file is created at the expected location in OneDrive but the file data is not identical to the original - and when opened in Excel, it sees it as corrupted.
So sending it via Postman:
this is the (simplified) Typescript code receiving it and passing it onto Graph API:
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const fileBytes = req.body;
const config:AxiosRequestConfig = {
headers: {
'Authorization': `Bearer ${bearerToken}`,
'Content-Type': 'text/plain'
}
}
const url:string = `https://graph.microsoft.com/v1.0/me/drive/root:/Testing/TDA/Test.xlsx:/content`;
const uploadResult:AxiosResponse<any, any> = await axios.put(url, fileBytes, config);
}
The file size is larger (22KB vs the original 18KB) and the encoding looks to be different as opening the original and its new copy in OneDrive in Notepad++ shows there are differences (one example highlighted in green):
What am I missing here? Is there a way I can ensure the format/encoding of the file uploaded and saved into OneDrive through Microsoft Graph API ends up being identical (in binary terms) to the original file sent through Postman?
Cheers - Nicolaas

How to download a multipart wav file from cloudant database and save locally using Node JS and REST API?

I am stuck in retrieving multipart from cloudant using Node JS API. Hence, I used REST API to download the wav file from cloudant database. But its not downloading wav file from https URL. When I enter the https URL directly in browser, it prompts me to save file locally. So, the URL is correct.
Here is the code for REST API:
var request1 = require('request');
var filestream = fs.createWriteStream("input.wav");
var authenticationHeader = "Basic " + new Buffer(user + ":" + pass).toString("base64");
request1( { url : "example.com/data/1533979044129/female";, headers : { "Authorization" : authenticationHeader } },
function (error, httpResponse, body) {
const statusCode = httpResponse.statusCode;
httpResponse.pipe(filestream);
httpResponse.on('end', function () {
console.log("file complete");
filestream.close();
}); });
The file size of input.wav is 0. Its not downloading file. Please help.
Your callback has an error argument, which you are completely ignoring. Do something with this error, like print it out so your problem can tell you what you're doing wrong. I definitely see at least 1 problem in your source, and the error from request should tell you what it is.
Edit On second thought the above code shouldn't even execute. You should share code that you tested yourself. There's typos in there.

Slack Attachment error : private_url is undefined

I have a simple code that's supposed to download images from slack messages.
var url = message.file.private_url;
var destination_path = './tmp/uploaded';
var opts = {
method: 'GET',
url: url,
headers: {
Authorization: 'Bearer ' + process.env.botToken,
}
};
request(opts, function(err, res, body) {
console.log('FILE RETRIEVE STATUS',res.statusCode);
}).pipe(fs.createWriteStream(destination_path));
The code worked fine for a while, but now I'm getting this error:
An error occured in the receive middleware: TypeError: Cannot read property 'private_url' of undefined
Any help would be appreciated!
Are you using the events API?
Several changes have been made to the API recently (both Events and Web APIs). See here: https://api.slack.com/changelog/2018-05-file-threads-soon-tread
If you describe the API you're using, I might be able to provide more specific help but I suspect the issue (as described in the link above) is that the file attribute attached to messages has been replaced with a new files field (an array). The files in the array are also in a different format.
Check the JSON payload. It probably contains a files array.

CKAN resource_create API

Am trying to read from list of files placed in a location using NodeJS and create resources for those files in CKAN.
Am using CKAN API v3 (/api/3) to create the resource.
NodeJS library used for iterating through files in a location : filehound
CKAN API Used : /api/3/action/resource_create
Code to iterate over files (please don't run it; it's only a sample snippet) :
// file operations helper
const fileHound = require("filehound");
// filesystem
const fs = require("fs");
// to make http requests
const superagent = require("superagent");
var basePath = "/test-path", packageId = "8bf37d22-0c25-40c0-8faa-7de12ff927f5";
var files = fileHound.create()
.paths(basePath)
.find();
files.then(function (fileNames) {
if (Array.isArray(fileNames)){
for (var fileName of fileNames) {
superagent
.post("http://test-ckan-domain.com/api/3/action/resource_create")
.set("Authorization", "78d5d219-37de-41f7-9443-188bc564051e")
.field("package_id", packageId)
.field("name", fileName)
.field("format", "CSV")
.attach("upload", fs.createReadStream(fileName))
.end(function (err, res) {
if (!err) console.log(fileName ,"Status Code ", res.statusCode);
else console.log("Error ", err);
});
}
}
});
After iterating through the files and uploading them to CKAN by creating a new resource, CKAN responds back with "success: true" and "id" of the resource created with HTTP status code 200.
However, only a few resource(s) are created and few are not. Am using CKAN's frontend to verify if the resource was created under a package/dataset (As the response from the Resource Creation API is successful).
Is it a known issue with CKAN? or am i going wrong in my code?
You've probably solved this by now, but given that some packages are just missing but you only get 200 success messages, I'm wondering if your for loop just isn't working correctly. Be careful with the syntax; I think declaring var filename inside the loop like that might be wrong.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of

File Corruption when Uploading Excel files to Microsoft Graph API Beta

We're trying to upload Microsoft Excel file to OneDrive but the file gets corrupted every time we do so.
We've tried using [these instructions] to make a PUT request to the following permutations of settings:
Content-Encodings:
text/plan
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
POST bodies:
XLSX file bytes raw from disk
XLSX file encoded as UTF8 string
XLSX file encoded as base64
If we download the file that gets uploaded, it looks almost the same, but a few binary regions are different.
If you feel comfortable opening an Excel file off the internet, I've uploaded an example of the file we upload and the corrupted file OneDrive saves.
This has all the smell of a bug that can be fixed with a single parameter modification... I just can't figure out what it is.
Anyone have thoughts? Thanks!
Thanks #GSM. Here's our code in TypeScript.
var fileContent = FileSystem.readFileSync(localFile);
var url = `https://graph.microsoft.com/beta/me/drive/root/children/${doc.name}.xlsx:/content`,
var opts {
url: url,
method: 'PUT',
headers: [
'Content-Type': 'text/plain',
'Authorization': token
],
body: fileContent
};
var requestOpts = {
url: `https://${domain}${opts.path}`,
method: opts.method,
headers: {},
};
request(opts, cb);
The only difference I see is that you're using an alternate path to upload the file, which is also documented on the GraphAPI page. If we use the path you're using we get back the back the error message:
{
"error": {
"code": "BadRequest",
"message": "Entity only allows writes with a JSON Content-Type header.",
"innerError": {
"request-id": "2a2e7588-3217-4337-bee3-f8aff208510c",
"date": "2016-05-30T16:35:50"
}
}
}
..which is strange because it makes me expect that your code shouldn't have worked either.
Update -- the answer
By reading the file into a string and then writing it to the JSON object that defined the PUT parameters, we were corrupting it. We solved the problem by simply piping a file read stream right to the open HTTP request.
It would be easier to help if you posted your code.
However, here's some code that can be used to upload files to OneDrive. I tested it with your file and was able to upload and download just fine:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.AccessToken);
var byteContent = File.ReadAllBytes(#"C:\Temp\sheet-uploaded.xlsx");
var url = resource + "beta/me/drive/root:/Documents/sheet-uploaded.xlsx:/content";
var result = client.PutAsync(url, new ByteArrayContent(byteContent)).Result;
result.Content.ReadAsStringAsync().Dump();
}

Resources