Upload file to SharePoint via REST API with gulp spsave - node.js

I am trying to upload a file (PDF) from a local directory to a document library on SharePoint. I have a gulp build system in place that watches the folder for changes and runs a task that pipes the PDF stream through gulp-spsave
gulp-spsave is nice because it abstracts away what appears to be a pretty complex SharePoint REST interface, where I can supply my credentials, url, and upload folder and it does the rest.
However, I keep getting a 404. I'm new to SharePoint and don't really understand the URL structure, so I'm guessing I'm not supplying the correct info to the plugin.
This is the URL to the document library (which is named DST):
https://{subdomain}.{customdomain}.com/depts/indirectsales/DST/Forms/AllItems.aspx
And this is my gulp task:
gulp.task('upload', function() {
var catalog = paths.output + 'catalog.pdf';
return gulp.src(catalog)
.pipe(spsave({
username: '{user}',
password: '{pass}',
siteUrl: 'https://{subdomain}.{customdomain}.com/depts/indirectsales/',
folder: 'DST'
}));
});
I have tried other combinations of siteUrl and folder to no avail, such as:
siteUrl: 'https://{subdomain}.{customdomain}.com',
folder: 'depts/indirectsales/DST'
Does anyone know what the correct strings would be for siteUrl and folder given the document library URL?
EDIT: It looks like I'm trying to connect to an on-premises Sharepoint 2010 installation, and spsave works only for 2013/2016 and SPO. Does anyone know how to upload a file to a document library in SP 2010 using REST?

It turns out the only reason I was having problems with a standard REST request was because I wasn't authenticating to SharePoint with NTLM. I was able to do so with the http-ntlm module in node:
httpntlm.post({
url: 'http://domain.com/site/_vti_bin/copy.asmx',
headers: {
'SOAPAction': 'http://schemas.microsoft.com/sharepoint/soap/CopyIntoItems',
'Content-Type': 'text/xml; charset="utf-8"'
},
username: 'user',
password: 'pass',
body: soapEnv,
}, function(err, response) {
//callback
}

Related

Download file from HTTP Drive API v3 with real file name

I have a custom REST Service in which a user from our platform downloads packages and binaries, but the problem is that the GDrive API downloads the file with the FileID as the file name:
async Download(fileID, res, type='stream') {
var Google = await CloudStorage.Initialize();
https.get(Google.API.STORAGE.Files.Download(fileID), {
headers: {
"Authorization": "Bearer " + Google.AccessToken.token,
"Content-Type": "text/plain"
},
responseType: type
}, (resApi) => {
res.writeHead(resApi.statusCode);
resApi.pipe(res);
}).end();
}
Example: https://MyUrl.com/beta/plugins/download/ABCDE12345
Where ABCDE12345 is the FileID of the file required by Google Drive API in order to GET the file.
The pipe of the response from the API indeed makes the downloaded file be named ABCDE12345.
Is there a way to make the download similar as doing it directly from the Google Drive Link?
When you download the file from the "Download" button through the Google Drive link it does download the file with the real name... How could I achieve this with my endpoint?
So,I had to use my girlfriend as the "rubber duck" and explain her my code when it came to my head while explaining: Use a fake endpoint for the named files.
My solution was to trick the endpoint by assigning a filename in a path parameter:
router.get('/dl/:id/:filename', async (req, res) => {
CloudStorage.Download(req.params.id, req.params.filename, res);
});
Now, having the :filename as a parameter allows me to verify it against the GDrive API since it's available for the given ID
In the end, it is now used as: https://MyUrl.com/beta/plugins/dl/ABCDE12345/Real File Name.ext
I can verify if Real File Name.ext is the real file name, if it is, then download it and the endpoint will allow me to create a file with this name :)

nodejs - Getting 401 error trying to download docs revisions with Google Drive API export links

I need to download all the revisions of a google doc with the Drive API using nodejs but I don't understand how to authorize the request for the export links. Once I get the export link for each revision I call:
var options = {
url: 'https://docs.google.com/feeds/download/documents/export/Export?id=1DRl6rbcVuuLVyb_WlhBLiYiCByWcS2bKGlLIsn7E8_8&revision=1&exportFormat=txt', //example link
method: 'GET',
headers: {
Authorization: `Bearer ${jwToken}`,
},
}
request(options).pipe(fs.createWriteStream(mydownloadfilename));
where the "jwToken" is the token I use to get the revisions list so I guess it should be still valid. However, with this I get the 401-Unauthorized page. What am I doing wrong?
Thanks
According to the Drive API v3 documentation:
Revisions for Google Docs, Sheets, and Slides can't be downloaded.
So essentially, if the actual revision you want to retrieve is the file itself, then the method above is the correct one.
As for the authorization part, you will need to perform the Node.js Quickstart from here and follow the steps explained there.
Since you want to export the file, you will just need to modify the code and add this part:
function downloadDoc() {
var fileId = 'ID_OF_THE_DOC';
var dest = fs.createWriteStream('DESTINATION_OF_THE_OUTPUT_STREAM');
drive.files.export({
fileId: fileId,
mimeType: 'application/vnd.google-apps.document'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
}
Reference
Drive API v3 - Manage Revisions;
Drive API v3 - Files:export;
Drive API v3 - Quickstart;
Drive API v3 - Download a document.
I am facing this same problem. The solution is to use
OAUTH2 authorization for a user that has Edit or Owner permissions for a file
Get an access token that expires quickly
call the V2 URI (V3 does not work) for the file/rev to get "export links" 3) Call the correct export link for your format type
then you will get a randomized temporary redirect link from Google that you can then call to get the binary stream.
This is a great starting point for C# .NET -- windows oauth console app, if you want working code to do steps 1 and 2. I posted a working v2 code function here that you can put into the console app example.

How to add Test Case into TFS via REST

I'm implementing lib to export/synchronize test cases into TFS automaticaly during test run. One of my requirements is that I need to use NodeJS for that, so I decided to use TFS REST API. In my code I'm using "azure-devops-node-api " lib, I can connect and get different elements, no luck with adding test cases.
I've found on the web that TestCase is kind of WorkItem and as WI should be added. Unfortunately I didn't find a way to add one with azure-devops-node-api.
I tried also to send manually constructed json, unfortunatelly no luck with finding proper url to sent as I'm allways getting :
Error: {"statusCode":404,"body":"Page not found."
My example request:
request.post({
url: 'https://<url>/tfs/<default collection maybe?>/<project>/_apis/wit/workItems/test%20case',
headers: {
'Content-Type': 'application/json',
'Authorization':'Basic ' + this.encodePat('<my auth token>')
},
rejectUnauthorized: false,//add when working with https sites
requestCert: false,//add when working with https sites
agent: false,//add when working with https sites
json: rq
},function (response, err, body){
if (err) throw new Error(JSON.stringify(err));
console.log('Body:',JSON.parse(body));
console.log('Response:',response)
});
Does anyone know what wrom I'm doing or if azure-devops-node-api is able to add WorkItems ?
If you want to add a test case only just as a work item you have to use the template from this link Work Items - Create.
POST https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/${type}?api-version=5.0
You have to add to url "?api-version=X.0". The example:
https://<server name and port>/tfs/<collection name should be>/<project>/_apis/wit/workItems/$test%20case?api-version=3.0
Also you have to encode your pat by this template:
'Authorization':'Basic ' + this.encodePat(':<my auth token>')
Here you can find an example for a build task with node.js: https://github.com/ashamrai/AzureDevOpsExtensions/blob/master/CustomBuildTask/NewWICustomTask/index.ts

How to make all files accessible in a single Google Drive folder?

I am trying to access media files in a folder that is created by my webapp using the official google api library for node.js (https://github.com/googleapis/google-api-nodejs-client/).
On successful oauth, I create a folder MyAppFolder, and a media folder inside it.
The idea is that users will fill this media folder with whatever photos and videos they want, then my webapp will take them and display them on a page for the user to get an aggregate view of their media. I am able to get all the media files within the media folder. Here is my snippet of code:
async function getGoogleDriveMedia({ user, credentials }) {
// await prepareDrive({ user, credentials });
const rootFolder = await getRootFolder({ user, credentials });
if (!rootFolder) {
return;
}
const client = await createOauthClient({ user, credentials });
const drive = google.drive({
version: 'v3',
auth: client,
});
const mediaFolderRes = await drive.files.list({
q: `mimeType='application/vnd.google-apps.folder' and name='media' and '${
rootFolder.id
}' in parents`,
fields: 'nextPageToken, files(id, name, parents)',
});
const mediaFolder = _.get(mediaFolderRes, 'data.files[0]');
if (!mediaFolder) {
return;
}
const mediaRes = await drive.files.list({
q: `'${
mediaFolder.id
}' in parents and (mimeType contains 'image/' or mimeType contains 'video/')`,
fields:
'nextPageToken, files(' +
[
'id',
'name',
'webContentLink',
'webViewLink',
].join(', ') +
')',
});
return _.get(mediaRes, 'data.files');
}
The problem now is that I'm not able to display these media because they are not publicly accessible. Is it possible to make the MyAppFolder and everything within it accessible to the public with a single permissions update? Or do I need to do it per file?
I also did a check on the fields.files parameter on their API explorer: https://developers.google.com/apis-explorer/#search/drive.files.list/m/drive/v3/drive.files.list
There isn't something like a .previewLink or some other image URL field.
How can I show these images on my webapp?
Have a look at the file.thumbnailLink. It may have enough resolution for your purposes and it's public. NB imho, the fact that it's public is a security bug which might get fixed at some point in the future.
NB there is a bug in your code. You are fetching nextPageToken correctly, but then you aren't using it to test if there are more pages in the files list that you need to fetch.
Is it possible to make the MyAppFolder and everything within it accessible to the public with a single permissions update? Or do I need to do it per file?
No there is no way to update everything in a file at once. You are going to have to update each file. Even updating the folder will not change the settings for the files with in the folder.
Just have your application change the permissions on the file after its uploaded. In the mean time run though all the existing files and change their permissions.
Note: I hope you have informed your users that their files will be made public.

How to create a Google Drive folder using node.js with googleapis official module

I'm trying this way:
var oauth2Client = new OAuth2(GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET, GDRIVE_REDIRECT_URI);
oauth2Client.setCredentials({
access_token: some_valid_access_token
});
drive.files.insert({
auth: oauth2Client,
resource: {
mimeType: 'application/vnd.google-apps.folder',
title: 'my new folder'
}
},function(err,response){
if(err){
console.log('error at gdrive creat folder: ' + util.inspect(err));
}else{
console.log('create response: ' + util.inspect(response));
}
});
And getting an error:
You cannot upload content to files of type application/vnd.google-apps.folder
What's the correct way of doing this?
The error message means that you (or the library on your behalf) is trying to upload content. This makes sense for regular files, but not for folders which have no content.
So either:-
You're not using the library correctly and there is a content-less insert you should be using
It's a bug in the library
I would suggest raise an issue on the GitHub project https://github.com/google/google-api-nodejs-client and update this question with whatever response you get.
If it's holding you up, just replace the library call with a really simple POST to the Drive endpoint with your "resource" as the POST body. You might find it's so easy that you throw the library away and do it all yourself.

Resources