TL;DR — I wrote an entity mapper in TypeScript that leverages abstract classes and works great with SharePoint. This technique probably applies equally well to other back end domains. TypeScript is a great enabler for this kind of tool.
If you just want to get to the code, skip this section.
For years, we SharePoint developers have been writing applications that view SharePoint, in part, as a database. For the many of you that don’t know much about it, SharePoint is a collaboration platform that supports all kinds collaborations stuff, social things, workflows, doc management and related yadda yadda yaddas.
It also has this notion of a “List” which acts a lot like a database table. MSFT provides APIs that lets us perform the usual CRUD operations against them. List items, however, are really higher level entities than DB tables. They can be secured via API, they can be tagged socially (ratings and likes). They can be approved, involved in workflows and other yadda yadda yaddas. SharePoint provides a pretty functional UI around list items as well.
In the early days of SharePoint client-side dev, we tended to use tools like SPServices, put together and managed by the ever-so-clever Marc D Anderson. Without his tool, you’d write a lot of $.ajax() calls to endpoints, parse an XML or JSON response, hope it was well-formed, etc. People still use it. It’s great.
About two years ago, before Microsoft released “SharePoint Framework” (or SPFx, their newest a shiniest SP dev tool), I started working with TypeScript. There were no abstract classes at the time and that affected how I work with it.
When I had to write an end user feature that worked with SharePoint lists, I would start off with a kind of model object, informed by the list’s columns. We might create a SharePoint list list to support a scrolling news ticker as follow:
Here’s a dead simple TypeScript model for that list:
The little screen shot above show that a “list helper” object is able to perform all of those operations against “news items.”
To enable this, I had to extend the model with new functionality. I don’t have a good intermediate step from the basic model above to the current state of my code base, so this listing is a bit of a humdinger:
Let’s break it down into smaller bits:
The top of the file imports a few objects and utility classes:
Here’s the next chunk:
First, it defines an interface for the model at line 1 in the listing . Note that it defines an interface that extends another interface, “ISharePointListBackedEntity”. This is important since any entity that implements that interface can now use all of the SharePoint list operations shown above. (If you’d like to detour into an explanation about abstract classes, try this blog post + video combination I posted a little while back)
It defines the core model properties, such as Title, Effective and Expiration Dates and such.
The entity’s constructor invokes the super’s constructor at line 22 and then assigns useful default values to the properties.
Here’s the final chunk:
This section of code defines a static method, GetSharePointMetadata(). As you’ll see, the generic SP List Helper uses this metadata to do the mapping at runtime between the SharePoint list’s columns and this entity’s properties.
There’s a lot going on. Let’s first look at line 10. This defines the actual mappings and does so by populating an array, colMappings. Here’s a typical mapping:
This defines a mapping between a SharePoint list column named “ID” and a model property, “SharePointID”). At runtime, the code populates the SharePoint “ID” list column into the model’s “SharePointID” property for read operations.The optional 3rd parameter, “EntityReadWriteAccess.READ_ONLY” indicates that this mapping is ignored when writing back to SharePoint. (The default is both Read and Write, that’s why you don’t see it specified on the other properties).
The low-level mapping is accomplished via a collection of static methods on the EntityMapper class. There is one associated method for each SharePoint data type. These mapplet functions understand how to translate the JSON response of an integer to a TypeScript number, a SharePoint JSON MMS object into an array of strings, etc.
There is one final mandatory link — the name of the SharePoint list itself. This is accomplished on line 8.
There are a few final things going on here beyond straight mapping.
At line 4, I define a “console logging label”. This is used by the helper to emit log messages with a label that distinguishes it from other helper’s that might logging out to the console.
Lastly, there is this line:
At runtime, it’s helpful to create an empty instance for the purposes of validating the integrity of the mapping. That validation is implemented at run-time by these functions:
EntityHelper.GetIntegerMapping(“ID”, “SharePointID”, …);
EntityHelper.GetIntegerMapping(“ID”, “SharepointID”, …);
In the “good” case, ID maps to SharePointID.
In the bad case, SharepointID is mis-spelled. The runtime validation logic detects this problem and warns you about it.
The fundamental idea is that we can create a model.ts file that connects the model to the SharePoint list in a more declarative way. By setting up the colMappings array with both the mappings (e.g. “VP_EffectiveDate” ← → “Effective Date”) as well as the appropriate serialization / deserialization function, we no longer need to concern ourselves with parsing JSON, constructing urls to correctly hit REST endpoints and the like. There is one source of truth as far as the entity mapping is concerned.
Being able to validate mappings at runtime is the icing on the cake.
Here’s a short video showing what it’s like to work with tools like this:
There are several really nice consequences that come of using abstract classes like this:
This entity mapping code evolved organically and was a great learning experience. It works well, but there are some obvious opportunities to improve it.
It would be nice to do something like this:
Basically, use TypeScript Decorators to specify entity mappings rather than the static method I’m using.
I recently published a book on TypeScript! It’s free and you can access it here: https://www.gitbook.com/book/pagalvin/yet-another-typescript-book/details
Create your free account to unlock your custom reading experience.