paint-brush
Useful Entity Mapping Supporting SharePoint Using TypeScript … and More???by@pagalvin
1,926 reads
1,926 reads

Useful Entity Mapping Supporting SharePoint Using TypeScript … and More???

by Paul GalvinJuly 25th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Useful Entity Mapping Supporting SharePoint Using TypeScript … and More???
Paul Galvin HackerNoon profile picture

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.

History

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.

Basic Models and Entity Mapping

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:

  • Title: string
  • Description: multiple lines of text
  • Effective Date: Date
  • Expiration Date: Date
  • Display Sequence: number

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:

  • EntityHelper: home to some static utility classes used further down the model
  • EntityReadWriteAccess: Denotes whether some properties should be read AND written to SharePoint or just read. For instance, we don’t want to write back SharePoint IDs.
  • AbstractViewPortItem: This contains a collection of properties that are common across a family of object (news items, featured news, favorties and the like).
  • SFInterfaces: defines a handful of common interfaces.

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:

The generic entity helper invokes these validation functions at run-time to help reduce the common problem of working with case-sensitive field names and tricky spellings that arise when you’re forced to match text strings in JSON to class properties or other JavaScript variables.These validation functions will throw an error if we try to map a class property, “xyzzy” to a SharePoint list column that doesn’t exist, or vice-versa. It will catch an invalid mapping like this:

Good:

EntityHelper.GetIntegerMapping(“ID”, “SharePointID”, …);

Bad:

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.

Practical Effects

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:

  • Strongly typed results.
  • Automatic mapping to SharePoint list columns.
  • Consistent CRUD+ operations. It’s always “helper.SaveItem()” or “helper.DeleteItem()”.
  • Fixing a bug for one method fixes it for all. I recall working on a project where I’d made a common error in one of those early factory mappers. I had to make the same update to a dozen factories, introduced a new error in a couple while I was doing it, etc.
  • Adding a new capability for one model entity means that all model entities get to enjoy it. For instance, when I started off with this, I was only focused gets, saves and deletes. At one point, I needed to set security. I enhanced the core splisthelper object with a security function and now all models can be secured.

Sensible Next Steps

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.

</end>

<postscript>

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

</endForReal>