Hot-patching our dependencies with patch-package

lego-644x450.jpg

This is also a quick blog post.

Let me tell you the tale of patch-package. Not many people have heard the tale of patch-package. Many prefer to walk the path forked.

Well, the path of patch-package works like this.

About patch-package

Your favourite NPM library has a problem. You need to hot fix it. You send in a PR and it’s stuck in review hell. The poor maintainer is on stress leave and you really don’t want to trouble her/him - they are in Finland, don’t wear shoes, and can’t possibly scale any further.

This is the patch-package path https://www.npmjs.com/package/patch-package

  1. yarn / npm lock the dependency on that specific version so our entire team and the build pipeline gets the same version

  2. npm install patch-package --save-dev

  3. make changes in node_modules against that library, e.g. “package-banana”

  4. npx patch-package “package-banana” - this creates a folder
    /patches/package-banana-version.patch

  5. add “postinstall": "patch-package" to scripts section in package.json

  6. check in / commit everything. Package.json and /patches/package-banana-*.patch

Now when anyone get latest and run npm install, the build chain will automatically grab that version of the library package-banana and apply your patch.

Then it will build as normal. This works great on Azure DevOps (VSTS) too.

When the library package-banana is finally fixed - update the library version and the patch file won’t work against newer versions.

Bad JavaScript. Expected '(' in our webpack eval chunk

This is a quick blog post for resolving an issue this morning from webpack uglified/minified JavaScript. The error happens in IE and Edge, it simply says:

Expected ‘(‘

This was happening in the middle of a webpack minified chunk so the entire block was wrapped inside eval(““)

Steps

  1. Navigate to the end of the webpack chunk, which will tell us the source of this chunk. Scroll past the sourcemap section to the end of the chunk (not the end of the file).

  2. Find the original file that the chunk was created from - now run ESLint over it.
    I didn’t have this handy in the build chain, so I ran it online https://eslint.org/demo/

  3. The error was spotted as there was a catch{} statement - in plain JavaScript - catch has to collect an argument. So the first syntax is incorrect. It must be the second syntax.

try {
    // do something
}
catch {
    // incorrect  
}
  
try {
    // do something
}
catch(e) {
    // correct, even if we don't use e  
}


Modern browsers are more lenient so this error did not appear on all browsers.

JSON cheatsheet for Microsoft Flow

I have a big blog post in the works for a significant dive in Microsoft Flow functionality.  But I realized that before we get there, we need to GET GOOD at doing JSON in Microsoft Flow.

So this is the cheat sheet.  With all the caveats.  Enjoy!

Update: added a compound JSON object construction.

This is the part of the cheatsheet series on Microsoft Flow.

  1. JSON cheatsheet for Microsoft Flow (this article)
  2. Nested-Flow / Reusable-Function cheatsheet for Microsoft Flow
  3. Building non-JSON webservices with Flow
  4. One Connection to Proxy Them All - Microsoft Flow with Azure Functions Proxies
  5. Building Binary output service with Microsoft Flow

var X = { "x": 1 }

{ "x": 1 }

And this is the running results

X = JSON.parse("{ 'x': 1 }")

Flow-2.png
"@json('{\"x\": 2}')"

X is updated.  But the run result panel for Set variable shows incorrect INPUTS value of X

{ ...X, { 'y': 2 } }
jQuery.extend( X, { 'y': 2 } )

"@union(variables('X'), json('{\"y\":2}'))"

Union is great - it'll let you add properties to existing objects easily.


var Y = { ...X, { 'y': 2 } }

Flow-4a.png

Use union to set variable

if ( Y.x != null )

sometimes, you need to check if an object contains a property

sometimes, you suddenly don't need the " double quotes for conditional statements. 
sometimes I feel this is a bug.

Flow-5.png

 

The UX can get quite silly.  It forgets how to render the basic view after you save.

(Y.x || Y.y)

"@coalesce(variables('Y').x, variables('Y').y )"

 

Use Coalesce to pick the first non-null value.  If the property doesn't exist this will error.  Protect the check with contains

X = Y

And we end this cheat sheet on a simple one.  Assigning variables to another.

Note you can't currently use the variable in setting the variable itself.

This would have been REALLY useful...  Because this would have let us keep appending to existing variable.

 

Finally, object composition
var Z = { z: Y }

"@json( concat( '{ \"z\": ', variables('Y'), ' }'  ))"

The result, see how a compound JSON object has been constructed.

April PnP JavaScript special interest group call and Azure Functions demos

Shortly after the March Azure Functions demo, I reached out and asked Patrick about coming back to do a follow up focused on JavaScript - specifically PnP-JS-Core.  As I've completely skipped it in the March call/demo that was focused on PnP PowerShell (and C#).  When I first started playing with Azure Functions I was doing everything in JavaScript - so it was nice to return to be able to do this demo. 

I'm a bit more mindful of the time, but this whole demo is on PnP-JS-Core.

We focused on a few things that people asked in the PnP-PowerShell call in March:

  • What about JavaScript - can you show JavaScript in Azure Functions
  • Isomorphic PnP-JS-Core - running on NodeJS - if you are going to use JavaScript on the client, might as well use the same code on the server.
  • Authentication using Sergei's node-sp-auth (congrats on MVP award!)
  • How to test your Azure Functions locally via azure-functions-cli
  • Live debugging with VSCode (locally)
  • How to pack your JavasScript AzureFunctions so that you don't need to deploy the massive node_modules (which is both costly for storage, and has a higher startup time).  We use azure-functions-pack

SharePoint's Future is full of JavaScript

Lots of quick little demos that makes a nice introduction scenario - but if you have not seen Azure Functions before, this is best viewed as a supplementary follow up to the first PnP Call in March.

Related Links

http://johnliu.net/blog/2017/4/march-pnp-special-interest-group-call-and-azure-functions-demos

 

Working with SharePoint WebHooks with JavaScript using an Azure Function

This blog post covers additional explanation on how to subscribe to SharePoint WebHooks and have it running with only JavaScript in Azure Functions.  The entire code is in one single JavaScript file.

The SharePoint team delivered on the promise to ship SharePoint WebHooks, and made an reference example in C#.  Be sure to watch the PnP webcast as well.

What is Azure Functions?

Azure Functions, in the simplest sense, is a Azure WebJob that lets you run a JavaScript function in a file (it can do C# too), and it will run it for you when you trigger it.  Because it is a Serverless platform - you don't pay for the WebJob unless your function is running.  This makes it really economical (you also get like a million free runs per month...)

Azure Function comes with a super user-friendly UI and you can paste or upload your JavaScript directly in the browser.  You don't need any tools installed.

Running in the browser

 

Of course, this is server-JavaScript.  So think NodeJS.  We can talk to lots of REST APIs (Graph, SPO) but there will be no browser DOM.

What can you do with this?

You can trigger it on a timer.   Hey that sounds like a SharePoint Timer Job.

You can trigger it on a web request.  This is super useful if you have a client-side UX and you need a button to do some high level elevated permission action.  You call the Azure Function to do it for you, using an App-Only permission elevation.

Why is SharePoint WebHooks important?

A SharePoint WebHook is a REST endpoint that you can attach a remote End Point to.  Right now, there is only a List endpoint.  So any updates to the list items will trigger an event, and SharePoint will call your function.

Hey.  Wait that sounds like a SharePoint Remote Event Receiver.  You are right!

Design

So the idea of our function is this - when triggered:

It will Auth and then talk to SharePoint REST and do one of the following.

  • Check if it has a request parameter "subs" - then it will list the current subscriptions on our target list
  • Check if it has a request parameter "sub" - it will try to attach itself to the target list
    SharePoint will immediately call the function with a validationtoken parameter so…
  • Check if the request has a validationtoken parameter - it will immediately reply with that token as text/plain.
  • Skipping all three conditions, it will run the default action
    The default action is that it will add an list item in a different destination list.  Because the function is running on its own App-Only permission, it can update a list that the original user doesn't have access to.

 

GET Subscriptions

options = {
    method: 'GET',
    uri: "https://johnliu365.sharepoint.com/_api/web/lists/getbytitle('subscribe-this')/subscriptions",
    headers: headers
};
request(options, function (error, res, body) {
    context.log(error);
    context.log(body);
    context.res = { body: body || '' };
    context.done();
});

GET subscriptions.  Array result is [] empty by default.  It has subscriptions after you attach hooks successfully.

 

POST Subscription (to add itself)

options = {
    method: 'POST',
    uri: "https://johnliu365.sharepoint.com/_api/web/lists/getbytitle('subscribe-this')/subscriptions",
    body: JSON.stringify({
        "resource": "https://johnliu365.sharepoint.com/_api/web/lists/getbytitle('subscribe-this')",
        "notificationUrl": "https://johnno-funks.azurewebsites.net/api/poke-spo2?code=q8sq9wxm62asd-YOURTRIGGERCODE",
        "expirationDateTime": "2017-01-01T16:17:57+00:00",
        "clientState": "jonnofunks"
    }),
    headers: headers
};
request(options, function (error, res, body) {
    context.log(error);
    context.log(body);
    context.res = { body: body || '' };
    context.done();
});

Sending POST subscription without handling validation token.  The request fails.

 

Handle Validation Token

// if validationtoken is specified in query
// immediately return token as text/plain
context.log(req.query);
context.log(req.query.validationtoken);
context.res = { "content-type": "text/plain", body: req.query.validationtoken };
context.done();

The function is called twice.  Second time by SharePoint to validate the subscription.

Result Video

The Poked list item was created by "SharePoint App" not "John Liu" the user.

Notes

This demo builds on top of the code from Azure Functions, JS and App-Only Updates to SharePoint Online that covers authentication with certificate, and running AppOnly permissions. 

Additionally, I've moved ClientID and Certifate ThumbPrint to Azure Function App Settings.  This means they are no longer part of the code.

App Settings

var clientId = process.env['MyClientId'];
var thumbprint = process.env['MyThumbPrint'];
Function App Settings > Configure App Settings

Function App Settings > Configure App Settings

 

Source Code

I'm making an effort to put all my demo source code on GitHub going forward.  This is part of the "Upgrade Your JS" demos.

https://github.com/johnnliu/demo-upgrade-your-js/tree/master/azure-function-web-hook

Let me know what you think about SharePoint WebHooks, Azure Functions and JavaScript that will rule everything ;-)

If you spot a bug or want to update the code - send me a Pull Request.