Last month I encountered a strange issue with the SharePoint Online REST API when trying to implement a feature for a social intranet we are building for one of our customers. The feature depends on the SocialRestFollowingManager resource, as described on this page on docs.microsoft.com.

I had already used this particular piece the REST API in other projects in SharePoint 2013 Server using the client-side coding library. Everything seemed to be working at first sight, as I could leverage this functionality to follow objects in SharePoint Online: sites, tags and documents.

The snag I hit was when I was trying to follow and unfollow documents specifically. As we have probably all noticed, the following of a single document from a document library has disappeared from the context menus in the SharePoint Online modern experience. This might already be an indication that Microsoft is not really willing to continue supporting this otherwise brilliant feature. We have had a lot of positive customer feedback on our implementation of this feature, where we can create a centrally located pinboard containing the direct links to all of the SharePoint content that you are following.

Whatever! Dive in to the code already!

When we take a look at the code, the funny thing is that it seems to work at first. The following of a document is done through the SharePoint UI: by switching a SharePoint document library to the classic view and using the context menu to follow the item (‘Volgen’ is Dutch for ‘Follow’):

Now for the ‘unfollowing’:

We use the function stopFollowDocument  to unfollow this document. In this function, we create an async ajax call to the social REST API, located under /_api/social.following containing the json-ized object (our document’s information) that needs to be unfollowed. The call is executed and the ajax call proceeds to its ‘success’-function. So far so good! But when we proceed to check if the document is now ‘unfollowed’, which we can do through the REST API url /_api/social.following/my/followed(types=2), we can see that actually nothing happened: the document is still being followed! Let’s bear in mind that the same code does work for tags and sites, but as shown, not for documents:

Here you haz teh Code

var documentUrl = "https://TENANTNAME.sharepoint.com/Gedeelde%20%20Documenten/demofile.txt";
var followingManagerEndpoint = "https://TENANTNAME.sharepoint.com/_api/social.following";

// Make the current user stop following a document.
// The request body includes a SocialActorInfo object that represents
// the document to stop following.
function <strong>stopFollowDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/stopfollowing",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    }),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function () {
      alert('The user has stopped following the document.');
    },
    error: requestFailed
  } );
}
 
function requestFailed(xhr, ajaxOptions, thrownError) {
  alert('Error:\\n' + xhr.status + '\\n' + thrownError + '\\n' + xhr.responseText);
}

// Make the current user start following a document.
// The request body includes a SocialActorInfo object that represents
// the document to follow.
// The success function reads the response from the REST service.
function <strong>followDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/follow",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    } ),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function (responseData) {
      stringData = JSON.stringify(responseData);
      jsonObject = JSON.parse(stringData);
      var statusMessage = {
        0 : 'The user has started following the document. ',
        1 : 'The user is already following the document. ',
        2 : 'An internal limit was reached. ',
        3 : 'An internal error occurred. '
      }
      alert(statusMessage[jsonObject.d.Follow] + 'Status code = ' + 
      jsonObject.d.Follow);
    },
    error: requestFailed
  } );
}

 

 

Now for the other way ‘round: when I use the followDocument() call to try to follow the document (whether it was already followed or not), I get a 404 error back through the error function ‘requestFailed’:

followDocument -> Error:\n404\n\n{“error”:{“code”:”3, Microsoft.Office.Server.Social.SPSocialException”,”message”:{“lang”:”en-US”,”value”:”The target of the action was not found. Internal typename: Microsoft.Office.Server.UserProfiles.FollowedContentException. Internal errorcode: 12.”}}}

Isn’t this weird? What’s up Microsoft, are you secretly getting rid of (parts of) this API?

Workaround

Fortunately we can work around this issue by leveraging the SP.Social.SocialFollowingManager in our code instead of the REST API:


var context = SP.ClientContext.get_current();
var socialManager = new SP.Social.SocialFollowingManager(context);
var socialObject = new SP.Social.SocialActorInfo();
socialObject.set_actorType(objectType);
var result = socialManager.follow(socialObject);
context.executeQueryAsync(…);

 

Conclusion

It seems wise to start refactoring your ‘social’ code for SharePoint Online to use the SP.Social.SocialFollowingManager instead of using ajax REST API calls to the /_api/social.following endpoint. Still I haven’t seen an official statement of Microsoft saying that this feature will be discontinued or whether this might be a bug. Tech support of MS wasn’t able to give me an answer to this question.