Integrating CRM Online and SharePoint Online: I need a hammer!

Last week I have been working on a project in which I had to integrate SharePoint Online within CRM Online. I was excited because I had to enrich data in CRM with data stored in SharePoint. Both running on line :)
When I started I thought that it was going to be a fairly simple job, as Microsoft provides the Microsoft.SharePoint.Client dlls. At first I built a console application in which I would receive the data from SharePoint.

SecureString securePassword = new SecureString();
foreach (char c in password.ToCharArray()) securePassword.AppendChar(c);

SharePointOnlineCredentials credentials =  new SharePointOnlineCredentials(userName, securePassword);
ClientOM.ClientContext context = new ClientOM.ClientContext(siteUrl);

context.Credentials = credentials;

ClientOM.Web web = context.Web;
context.Load(web);
ClientOM.List lst = web.Lists.GetByTitle(listName);
context.Load(lst);
... and so on ...

I wrote the logic, tested it, embedded it inside the CRM plugin, deployed it to CRM online and I was good to go… 

All hell broke loose

As soon as I started to test my plugin, all hell broke loose. CRM crashed so badly that even the exception handling I implemented wasn’t triggered! This can only be caused by low level calls (probably in underlying COM functionality). NASTY!
The plugin actually crashed on the very first SharePoint related statement setting the “SharePointOnlineCredentials” object. Nothing fancy one would say.
I ran into a solid wall. If I couldn’t use the client side object model, how do I had to get my data?

REST to the rescue

After doing some research, it turned out that REST was the way to go. Using rest means that I’m not dependant on the SharePoint client dlls, it also means that I have to do the plumbing myself. During my research I stumbles upon some C# classes built bij SharePoint developers to facilitate the REST operations on SharePoint. This included authentication as well. I had to make a couple of small modifications to let the functionality land on the sandbox of CRM online.

In the File Attachment: SharePointHelpers.zip (7 KB), you’ll find the modified classes. You can use the classes by making the following call.

Uri spSite = new Uri(siteUrl);

bool success = SpoAuthUtility.Create(spSite, userEmailAddress, WebUtility.HtmlEncode(password), false);

if (success)
{
Uri url = new Uri(String.Format("{0}/{1}", SpoAuthUtility.Current.SiteUrl, restQuery));
Dictionary<string,string> headers = new Dictionary<string,string>();
headers.Add("binaryStringResponseBody","true");

// Send a json odata request to SPO rest services to fetch all list items for the list.
byte[] result = HttpHelper.SendODataJsonRequest(
url,
"GET", // reading data from SP through the rest api usually uses the GET verb
null,
(HttpWebRequest)HttpWebRequest.Create(url),
SpoAuthUtility.Current, // pass in the helper object that allows us to make authenticated calls to SPO rest services
headers // specify that sharepoint needs to return uncontaminated bytes....
);

string response = Encoding.UTF8.GetString(result, 0, result.Length);
Console.WriteLine(response);
}

Finally I tackled the problem, but… some days you really wish you had a big hammer!

13 thoughts on “Integrating CRM Online and SharePoint Online: I need a hammer!

  1. Morne says:

    Thank you for the post. Please can you post an example for creating a folder and also uploading files using your lib and REST

    • Hi Morne,

      I’ll see if I can post a sample this week. I’m on a tight schedule right now

      • Morne says:

        Thank you. I tried to get an application up and running, but have trouble with the headers and also getting a exception about serialization

        • can you post some code?

          • Morne says:

            I’ve replace some of the settings, urls, usernames and passwords..

            Uri spSite = new Uri(“https://abc.sharepoint.com”);

            bool suceess = SpoAuthUtility.Create(spSite, “mwolfaardt@domain.com”, WebUtility.HtmlEncode(“P@ssword”), false);
            if (suceess)
            {
            Uri url = new Uri(String.Format(“{0}{1}”, SpoAuthUtility.Current.SiteUrl, “_api/web/folders”));
            Dictionary headers = new Dictionary();
            headers.Add(“binaryStringResponseBody”, “true”);
            headers.Add(“X-RequestDigest”, SpoAuthUtility.GetRequestDigest());
            headers.Add(“accept”, “application/json;odata=verbose”);
            headers.Add(“content-type”,”application/json;odata=verbose”);

            string bodyQuery = “‘__metadata’: { ‘type’: ‘SP.Folder’ }, ‘ServerRelativeUrl’: ‘/Appointment/49788571-0623-e511-80e0-3863bb355d80′”;

            byte[] body = Encoding.ASCII.GetBytes(bodyQuery);
            headers.Add(“content-length”, body.Length.ToString());
            // Send a json odata request to SPO rest services to fetch all list items for the list.
            byte[] result = HttpHelper.SendODataJsonRequest(url, “POST”, // reading data from SP through the rest api usually uses the GET verb
            body,
            (HttpWebRequest)HttpWebRequest.Create(url),
            SpoAuthUtility.Current, // pass in the helper object that allows us to make authenticated calls to SPO rest services
            headers // specify that sharepoint needs to return uncontaminated bytes….
            );
            string response = Encoding.UTF8.GetString(result, 0, result.Length);
            }
            else
            {
            throw new Exception(“Could not authenticate with SharePoint”);
            }

  2. Yuki says:

    I am also getting,

    Type ‘ContextInfo’ cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types. -2146233088

    when i try to
    string restQuery = “_api/web/getfolderbyserverrelativeurl(‘/Shared Documents/incident’)/folders(‘” + sDocumentLibrary + “‘)/ItemCount”;

    Uri url = new Uri(String.Format(“{0}/{1}”, SpoAuthUtility.Current.SiteUrl, restQuery));

    Dictionary headers = new Dictionary();
    headers.Add(“binaryStringResponseBody”, “true”);
    headers.Add(“X-RequestDigest”, SpoAuthUtility.GetRequestDigest());
    headers.Add(“accept”, “application/json;odata=verbose”);

    Would you please give me some clues?

    Thanks!

  3. Yuki says:

    Hi Bas,

    with your help I am able to list files in a sharepoint folder from the plugin but am having difficulty with a REST query like https://site.sharepoint.com/sites/somefolder/_api/Web/GetFolderByServerRelativeUrl('/sites/somefolder/incident/Badger_D312BBD6E443E51180D83863BB2EA878/logo.png‘)/moveto(newurl = ‘/sites/somefolder/incident/Maintenance _7957EE051C14E51180D33863BB368D68/logo.png’, flags = 1)
    to move a document from one folder to another

    do I need a byte[] requestContent

    have you a sample you could post ?

    Thanks,
    Yuki

    • Hi Yuki,

      I think you have to split up your action. First get the document, then insert it in the other library. Then remove the old document.
      That is the way I used to do it in the past

      Bas

Leave a Reply

Your email address will not be published. Required fields are marked *