People Sync: Creating a Custom Data Source - akumina/AkuminaTraining GitHub Wiki
Akumina Foundation 3.4.0.0 and later
You can download the code referenced in this article here
The Akumina Framework allows easy integration of Users from a variety of data sources into a Akumina Foundation Site. The AppManager has the powerful People Directory Sync app, a management app which enables an administrator to determine the set of users that are accessible to widgets on the site. Out of the box, the People Directory Sync App can pull users from Azure Active Directory or SharePoint User Profiles.
Users can also be defined within xml and synchronized to a Digital Workplace Site. Below is a sample user xml definition.
<Users xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<User ID="23497" DisplayName="Doe, John S" GivenName="John" MiddleInitial="S" Surname="Doe" Mail="[email protected]" JobTitle="Developer" Organization="Sample Organization 1" Section="Sample Organization Product" Division="North America" Department="Engineering" OfficeLocation="Nashua, NH"/>
</Users>
We can easily create a custom data source by creating a class that extends the IPeopleDirectoryUserFetcher interface.
using Akumina.Common.Utilities;
using Akumina.Interchange.Core;
using Akumina.Interchange.Core.Entities.Connector;
using Akumina.Interchange.Core.Interfaces;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Web;
using System.Web.Hosting;
namespace CustomPeopleFetcher
{
public class CustomPeopleFetcher : IPeopleDirectoryUserFetcher
{
IConnectorService ConnectorServiceInstance;
IPeopleDirectoryUserStorageHandler Storage;
private Dictionary<string, string> _options;
public CustomPeopleFetcher(IConnectorService _connectorService, IPeopleDirectoryUserStorageHandler _storageHandler)
{
ConnectorServiceInstance = _connectorService;
Storage = _storageHandler;
}
public PeopleDirectoryFetchResult FetchUsers()
{
PeopleDirectoryFetchResult result = new PeopleDirectoryFetchResult() { IsComplete = false, IsError = true, Message = "Unknown Error" };
#region Get all Users
System.IO.StreamReader reader = null;
try
{
Storage.Delete();
//Storing an xml file within our hosted files. Alternative approach would be to retrieve from a web url
var requestUrl = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "SiteDefinitions", "export.xml");
System.Net.WebRequest request = System.Net.WebRequest.Create(requestUrl);
var responseStream = request.GetResponse().GetResponseStream();
reader = new System.IO.StreamReader(responseStream);
string usersXml = reader.ReadToEnd();
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.LoadXml(usersXml);
List<PeopleDirectoryUser> aadUserList = Read(xmlDoc);
result.Users = aadUserList;
result.UserCount = aadUserList.Count;
result.IsComplete = true;
result.IsError = false;
result.Message = string.Empty;
}
catch (Exception e)
{
result.IsError = true;
result.Message = string.Format("\nError getting Users. {0} {1}", e.Message,
e.InnerException != null ? e.InnerException.Message : "");
}
finally
{
if (reader != null)
{
reader.Dispose();
reader = null;
}
}
#endregion
return result;
}
public void Initialize(Dictionary<string, string> fetcherOptions)
{
_options = fetcherOptions;
}
private List<PeopleDirectoryUser> Read(System.Xml.XmlDocument xmlDoc)
{
List<PeopleDirectoryUser> aadusers = new List<PeopleDirectoryUser>();
try
{
List<KeyValuePair<string, string>> properties = null;
foreach (System.Xml.XmlElement userNode in xmlDoc.SelectNodes("//User"))
{
properties = new List<KeyValuePair<string, string>>();
foreach (System.Xml.XmlAttribute userPropAttribute in userNode.Attributes)
{
properties.Add(new KeyValuePair<string, string>(userPropAttribute.Name, userPropAttribute.Value));
}
aadusers.Add(new PeopleDirectoryUser(properties.ToArray()));
}
}
catch (Exception ex)
{
aadusers.Clear();
//TraceEvents.Log.Error(string.Format("Error detail: {0}", ex.InnerException.Message));
}
return aadusers;
}
}
}
Extending the IPeopleDirectoryUserFetcher interface is required for the custom data source to interact with the People Directory Sync. It requires the following methods
void Initialize(Dictionary<string, string> fetcherOptions);
PeopleDirectoryFetchResult FetchUsers();
Here we fetch users from our custom data source and add them to a List list object.
Any additional options for the User Synchronization are set here.
As this current example is retrieving user data from an xml file, we've created the Read helper method to parse the xml information and return it within a List list object.
We will use the code snippets reviewed above to sync the John Doe user to our Akumina Foundation People Directory control. The following example will be done with a locally hosted instance of Interchange. The same steps can be followed for Azure Hosted Interchange by accessing your files through an FTP manager.
Copy and paste the xml snippet from the section above into an xml document. Save it as export.xml. Open your Hosted Interchange files. Copy your export.xml file and paste it inside of the SiteDefinitions folder within your Hosted Interchange files.
Create a new blank solution called CustomPeopleFetcher within Visual Studio. Within your new solution, add a new C# Class Library project called CustomPeopleFetcher. Within the project add a new C# class called CustomPeopleFetcher.cs and paste the c# code snippet from the section above. Ensure your project has the references highlighted below.
Save and build your solution. Open your solution in File Explorer and Navigate to your Debug folder at ...\CustomPeopleFetcher\CustomPeopleFetcher\bin\Debug. You will see your CustomPeopleFetcher.dll file.
Open your Hosted AppManager files, either in File Explorer if you are locally hosting or in an FTP manager if you are hosting on Azure. Within your Hosted Interchange files, navigate to the bin folder. Paste your CustomPeopleFetcher.dll within.
Within the root of your Hosted AppManager files create a folder called temp
Locate your interchange.settings.config file within the root of your Hosted AppManager files. Edit it within a text editor. Insert the following key between the <appSettings></appSettings>
tags.
<add key="akumina:tempuploadpath" value="C:\akumina\3.4.1703.1423-InterChange\temp" />
Change the value attribute to point to the temp file you just created.
Within AppManager, go to the People Directory Sync app inside the Management App tab. Under Select Data Source, select CustomPeopleFetcher. Click Synchronize.
Once the People Directory Sync has finished syncing users navigate to the Directory.aspx page on your Akumina Foundation Site. Refresh the Cache and the page. You should see the profile for your custom user John Doe.
You can download the code referenced in this article here