Dynamics 365 and SharePoint Online Integration – Web Api Development

Dynamics 365 and SharePoint Online Integration – Web Api Development

Now that we have configured our SharePoint and Azure environments to allow us to create an API that will be called from CRM and process SharePoint requests, we will dive into the actual API.

If you haven’t developed any API projects with Visual Studio, I would suggest familiarizing yourself with Web API, and take a look at the following Microsoft Documentation: Getting Started with ASP.NET Web API 2 (C#).

The Web Api projects contains the following components which we will get into further details in this post:

  • References to Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime
  • Reference to CAML Query Builder. This is a modified version of the CAML Query builder that is available on github: https://github.com/noormahdi/CAML-Query-Builder
  • Controller classes for DocumentLibrary and LookupItems
  • Model classes for LookupItem, SharePointDocument and SharePointFolder
  • Helper classes for dealing with File Upload, Data, Encryption, and SharePoint

As the API library contains a lot of different functionality, I will go through the process of retrieving the documents API to show in the subgrid. The full source code and instruction on how to use will be available soon on github, so that you can implement this in your own environment. We will start by taking a look at one of the HttpGet methods in our Api Library. This is the starting point that the user will execute against the Api and get a response of the list of documents that are available on SharePoint.

[HttpGet]
[Route("api/Library/GetByMasterId/{id}")]
public HttpResponseMessage GetByMasterId([FromUri] string id)
{
	string authorizationString = DecodeAuthorizationString();
	SPHelper.SetSharePointCredentials(authorizationString);

	List<SharePointDocument> files = new List<SharePointDocument>();

	ListItemCollection list = SPHelper.GetDocumentsById(id);
	if (list != null && list.AreItemsAvailable)
	{
		foreach (ListItem item in list)
		{
			SharePointDocument file = ListItemToSharePointDocument(item);
			files.Add(file);
		}
	}

	var response = Request.CreateResponse(HttpStatusCode.OK);
	response.Content = new StringContent(JsonConvert.SerializeObject(files), Encoding.UTF8, "application/json");
	return response;
}

The function starts by Retrieving the Authorization Credentials. This is either passed by the Web Resource from CRM as an encoded string as part of the request, or in our case stored in the application setting. The function checks if the Request.Authorization.Header contains a value. If it does it retrieves the string from the Authorization header, or if not it creates the Authorization string from the Application Settings Username and Password.

private string DecodeAuthorizationString()
{
	string userPass = string.Empty;
	if (Request.Headers.Authorization == null)
	{
		// throw HttpResponseHelper.GetUnauthorizedResponseException("Auth Header is null!");
		string emailAddress = ConfigurationManager.AppSettings["Email"].ToString();
		string password = ConfigurationManager.AppSettings["Password"].ToString();
		userPass = string.Format("{0}:{1}", emailAddress, password);
	}
	else
	{
		var authHeader = Request.Headers.Authorization;
		if (authHeader.Scheme.ToLower() != Constants.AUTH_HEADER.BASIC)
		{
			throw HttpResponseHelper.GetUnauthorizedResponseException("Auth Header is not using BASIC scheme!");
		}
		var encodedUserPass = authHeader.Parameter;
		userPass = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUserPass));
	}
	return userPass;
}

We then call the Set SharePoint Credentials function passing the Authorization string. This is using a Helper Class that handles with connectivity to SharePoint and executing SharePoint Client Commands against SharePoint.

public static void SetSharePointCredentials(string authorization)
{
	string[] authCredentials = authorization.Split(':');
	ServiceUserName = authCredentials[0].ToString();
	ServicePassword = authCredentials[1].ToString();
}

The GetDocumentsById connects to SharePoint (if not already connected), creates a CAML query with a where clause to search by the Master Id and returns a ListItemCollection.

public static ListItemCollection GetDocumentsById(string masterId)
{
	ClientContext ctx = ConnectToSharePoint();

	List spList = ctx.Web.Lists.GetByTitle("Documents");
	ctx.Load(spList);
	ctx.ExecuteQuery();

	if (spList != null && spList.ItemCount > 0)
	{
		string whereClause =  String.Format(@"<Where>
			<Eq><FieldRef Name='MasterId' /><Value Type='Text'>{0}</Value></Eq>
			</Where> ", masterId);

		CamlQuery camlQuery = new CamlQuery();
		camlQuery.ViewXml = String.Format(
		   @"<View Scope='RecursiveAll'>
			<Query> 
				{0}
			</Query> 
			 <ViewFields>
				<FieldRef Name='FileLeafRef' />
				<FieldRef Name='Title' />
				<FieldRef Name='MasterId' />
				<FieldRef Name='MasterNumber' />
				<FieldRef Name='MasterName' />
			<FieldRef Name='DocumentType' />
				<FieldRef Name='Created' />
			</ViewFields> 
	</View>", whereClause);

		ListItemCollection listItems = spList.GetItems(camlQuery);
		ctx.Load(listItems);
		ctx.ExecuteQuery();

		return listItems;
	}
	else
		return null;

}

We then loop through the items of the collection, and convert each ListItem to a SharePointDocument. SharePointDocument is a Model class that contains the fields that are returned from SharePoint, and will be returned to the web resource as a Json string. The function below shows the ListItemToSharePointDocument function which handles the conversion.

private SharePointDocument ListItemToSharePointDocument(ListItem item)
{
	SharePointDocument file = new SharePointDocument();
	file.FileId = (item["UniqueId"].ToString().ToGuid());
	file.MasterId = item["MasterId"] != null ? item["MasterId"].ToString() : "";
	file.MasterNumber = item["MasterNumber"] != null ? item["MasterNumber"].ToString() : "";
	file.DocumentType = FieldLookupValueToLookupItem(item["DocumentType"]);
	file.MasterName = item["MasterName"] != null ? item["MasterName"].ToString() : "";
	file.FileName = item["FileLeafRef"].ToString();
	file.FilePath = item["FileRef"].ToString();

	return file;
}

Finally, we create the response passing a Status of OK (regardless whether or not files were returned), and serialize the files object as a Json string and return the response.

The list of functions that are included as part of the Api are:

  • GetByMasterId
  • GetByMasterNumber
  • GetByAlternateField
  • DownloadFile
  • UploadDocument
  • SetFileAttributes
  • DeleteFile
  • GetRootFolders
  • GetFolder
  • CreateRootFolder

The next and final articles in this blog series will cover the creation of the web resource.