The first thing we need to do is to install the csom-node package in our NodeJS bot.
- Login to Azure portal
- Select your Web App Bot
- Click on Build under Bot Management from the left pane.
- Use Open online code Editor.
- Open kudu console
- It will open the console in a new window like below.
- Go to path - cd D:\home\site\wwwroot>
- Run install command
Alternatively, if you want to use app principal install it via the below command. This is a module from my GitHub repo. The reason behind using my repo is explained at the end of the article.
- npm install --save csom-node@siddharth-vaghasia/CSOMNode#master
After installation you will get a success message, for me, as it was already installed, it says it updated 1 package.
Go back to app.js, let us add the required code snippets.
The first thing we will do here is to create a function which will connect to SharePoint online by taking URL as parameters and return a callback function which returns web title. This will make sure we are able to connect to SharePoint Online.
Note
We could also ask the user to enter username and password one by one and then use it rather than hardcoding in code, but as it would have increase dialog flow code, I have kept it simple.
-
- function ConnectToSP(siteurl, fn) {
- var csomapi = require('csom-node');
-
- var settings = {
- url: siteurl,
- username: "[email protected]",
- password: "JokesOnYou"
- };
- csomapi.setLoaderOptions({ url: settings.url });
- var authCtx = new AuthenticationContext(settings.url);
- authCtx.acquireTokenForUser(settings.username, settings.password, function (err, data) {
- console.log(JSON.stringify(data));
- var ctx = new SP.ClientContext(siteurl);
- authCtx.setAuthenticationCookie(ctx);
-
-
- var web = ctx.get_web();
- ctx.load(web);
- ctx.executeQueryAsync(function () {
- console.log(web.get_title());
- fn(web.get_title());
- },
- function (sender, args) {
- console.log('An error occured: ' + args.get_message());
- });
- });
- }
Now, let us change our root bot dialog flow and ask the user to enter site URL and return the site title in response after the connection is sucessful.
- bot.dialog('/', [
- function (session) {
- builder.Prompts.text(session, "Hello... What's your name?");
- },
- function (session, results) {
- session.userData.name = results.response;
- session.send("Hi " + results.response);
- builder.Prompts.text(session,"Please enter SharePoint Online Site Colllection URL");
-
- },
- function (session, results) {
- session.userData.siteurl = results.response;
- ConnectToSP(session.userData.siteurl, function (siteTitle) {
- session.send("Connected - Your site name is " + siteTitle);
- });
- }
- ]);
Test your bot again and if everything is correct, it will show the below output.
Awesome, give yourself a pat on the back. We were able to connect SharePoint online site from the bot. Now, let us now extend to get all list and library names under a particular site collection.
First, let us add function to get List and Libraries. Please note we have filter list libraries which are not hidden.
- function GetLists(siteurl, fn)
- {
- var csomapi = require('csom-node');
- var settings = {
- url: siteurl,
- username: "[email protected]",
- password: "JokesOnYou"
- };
- csomapi.setLoaderOptions({ url: settings.url });
- var authCtx = new AuthenticationContext(settings.url);
- authCtx.acquireTokenForUser(settings.username, settings.password, function (err, data) {
- var ctx = new SP.ClientContext(siteurl);
- authCtx.setAuthenticationCookie(ctx);
- var web = ctx.get_web();
- var lists = web.get_lists();
- var libraries = [];
- ctx.load(lists);
- ctx.executeQueryAsync(
- function(){
- var listEnumerator = lists.getEnumerator();
- while (listEnumerator.moveNext())
- {
- var oListItem = listEnumerator.get_current();
- if(!oListItem.get_hidden()){
-
-
- if (oListItem.get_baseTemplate() == 101) {
- var libname = oListItem.get_title();
- libraries.push(libname);
- }
- else if (oListItem.get_baseTemplate() == 100) {
- var listname = oListItem.get_title();
- libraries.push(listname);
- }
- }
- }
- fn(libraries);
- },
- logError);
-
- function logError(sender,args){
- console.log(args.get_message());
- }
- });
- }
Modify the bot root dialog flow.
Please note that here, we are using builder.Prompt.choice to display choices: One without style so that the user has to type, and another one using button style, so that the user can select among the choices provided, like buttons.
- bot.dialog('/', [
- function (session) {
- builder.Prompts.text(session, "Hello... What's your name?");
- },
- function (session, results) {
- session.userData.name = results.response;
- session.send("Hi " + results.response);
-
- builder.Prompts.text(session,"Please enter SharePoint Online Site Colllection URL");
-
- },
- function (session, results) {
- session.userData.siteurl = results.response;
- ConnectToSP(session.userData.siteurl, function (siteTitle) {
- session.send("Connected - Your site name is " + siteTitle);
- builder.Prompts.choice(session, "Do you want to get name of list and libraries?", ['Yes','No']);
- });
- },
- function (session, results) {
- if(results.response.entity == 'Yes'){
- GetLists(session.userData.siteurl, function (arrayListLibraries) {
- builder.Prompts.choice(session, "Below are list and libraries in you site.", arrayListLibraries,{ listStyle: builder.ListStyle.button });
- });
- }
- else{
- session.send("Thank you, have a nice day.");
- }
- },
- function (session, results) {
- session.send("Connected - You have selected - " + results.response.entity);
- session.send("Thank you, have a nice day.");
-
- }
- ]);
Test the bot again. If everything seems correct, you will get the below output. The screenshot is after the bot is connected to SharePoint.
Before concluding the article, let us also see how can we use the app principal to authenticate and connect to the user. To connect via App Principal we should have client id and secret generated and also appropriate permission granted to the app. How to generate this is nicely described in this
article by
Nanddeep Nachan. Below is what I did at a high level for testing.
- Register new app on https://tenant.sharepoint.com/_layouts/15/appregnew.aspx
- Granted permission on https://tenant-admin.sharepoint.com/_layouts/15/appinv.aspx ( by using client Id generated from step 1)
I have provided full control permission but scope and permission can be given as per user requirements.
Once we have client Id and client secret noted down, we can use the below method in NodeJs to connect to SharePoint
- function ConnectToSPViaAppPrincipal(siteurl, fn) {
- var csomapi = require('csom-node');
- var settings = {
- url: siteurl,
- clientId: "CLIENTID HERE",
- clientSecret: "CLIENT SECRET HERE"
- };
-
-
- csomapi.setLoaderOptions({ url: settings.url });
- var authCtx = new AuthenticationContext(settings.url);
- authCtx.acquireTokenForApp(settings.clientId, settings.clientSecret, function (err, data) {
- console.log("got context");
- var ctx = new SP.ClientContext(siteurl);
- authCtx.setAuthenticationCookie(ctx);
-
- var web = ctx.get_web();
- ctx.load(web);
- ctx.executeQueryAsync(function () {
- console.log("query ");
- console.log(web.get_title());
- fn(web.get_title());
- },
- function (sender, args) {
- console.log('An error occured: ' + args.get_message() + args.get_stackTrace());
- });
-
- });
-
- }
Note
Please make sure site URL passed to the above method ends with 'slash'. If the URL does not end with slash '/', the acquireTokenForApp method will return a "404 file not found" error. I spent around 2 hours debugging this issue. So to save others' time, created a new branch with a fix. If you are going to use App Principal, install csom-node using the below command.
- npm install --save csom-node@siddharth-vaghasia/CSOMNode#master
Conclusion
In this article, we have learned:
- How to connect Azure bot with SharePoint online.
- What is Node JS FormFlow template?
- How to install node module in Azure bot framework.
- How to connect SharePoint online via User Credentials and via App only Principal.
This will give us basic direction on how to connect SharePoint online from Azure bot based on Node JS. You can extend this as per your requirement. Basically, you will be able to perform any operations on SharePoint online which are supported based on the client object model.
Hope this helps ... happy coding!