Wednesday, 10 April 2019

Service Locator pattern in SPFx: Using nested scopes to work with multiple components

In the previous post, we saw how SharePoint Framework code can be decoupled by using the Service Locator pattern and Service Scopes. In short, we are able to register instances of our services on the "global" service scope and then consume those instances from any part of our code.

We can also consume instances of default services registered on the global service scope e.g. MSGraphClient, AadHttpClient, SPHttpClient etc. These registrations are done by the SharePoint Framework and all we have to do is hook into the global service scope to fetch their instances.

Now, when we register our custom service on the global service scope, all components on the SharePoint page share a single instance of the service. By components, I mean SPFx web parts and extensions. In this post, to keep things simple, I will only talk about web parts but the concepts apply to extensions as well.

When the first webpart on the page makes a call to the service scope, an instance of the custom service is created and returned. Any further calls made by the same or different webparts on the page will return the same instance of the service. Now this behaviour can be either good or bad depending on your requirements.

If you want different webparts on the page to have their own instances of the custom service, let's see how to achieve this:

Let's consider a simple custom service:

The service contains a private variable count which is initialised to 0. The increaseAndReturnCount function increases the count by 1 and then returns the value. Not the best code ever written but fits our current purpose.

Now, let's register and consume an instance of our custom service from an SPFx webpart:

If you are wondering where do we register this service as we only seem to be calling the consume function. The consume function does the registration for us by creating a default instance of the service, registering it on the service scope and then returning the same instance.

When we register our service on the global service scope returned by this.context.serviceScope, all webparts and SPFx components will share the same service instance:



Although multiple webparts are added on the page, they all share the same service instance and the same count variable keeps on increasing. OK, technically I am adding the same webpart multiple times on the page but the behaviour is the same even for different webparts calling the same service.

Now, to get around this issue, we only need to make a simple change to our code. We don't need to change any of the service code, we just need to change the way in which the service instance is created and returned:

By creating a new service scope as a child of the global service scope, we get a new service scope which is isolated to the current web part. This way, each web part will get it's own service scope to register and consume service instances.

And as a result, we get a new instance of the counter service for each web part where the counter variable is initialised to 0:


This way, we are able to create and maintain isolated service scopes per component on our page.

Hope you found this post useful!

The code is in GitHub if you want to take a look: https://github.com/vman/MultiInstanceServiceScopes

Wednesday, 27 March 2019

Service Locator pattern in SPFx: Using Service Scopes

I have written about this topic in the past but with the recent increase in SharePoint Framework adoption and with more features becoming available (e.g. MSGraphClient, AadHttpClient), I felt it would be a good time to revisit this.

With SPFx solutions getting more complex day by day and with lots of components to manage, passing the web part (or extension) context around to different parts of your code can get difficult to maintain real fast.

The problem:


For example, imagine we have created a custom service which needs the MSGraphClient to make a call to the Microsoft Graph and we are consuming this service in our SPFx webpart. To initialise the service, we need to either pass in the entire web part context to it, or explicitly pass in the MSGraphClient object from the context.

In the first case, we are unnecessarily passing in all other objects in the context to this service as well.

And in the second case, our code becomes tightly coupled i.e. in the future, if our service needs something else from the webpart context, we have to update the service as well as the consuming code (i.e. the webpart) to pass in the new dependency.

The solution:


Using the Service Locator pattern in SPFx through service scopes, we can abstract away the implementation details of our custom services from the calling code (i.e. webparts, extensions).

With service scopes, we can get hold of instances of SPFx classes like MSGraphClient, AadHttpClient, SPHttpClient and PageContext from our services without having to explicitly pass them in from the webparts.



In this post, lets see how to achieve this:

1) Calling the MSGraphClient from a custom service:



Consuming the custom service from an SPFx webpart:


If you observe the code, our calling webpart does not have any knowledge of the implementation details of the custom service, specifically the fact that it used the MSGraphClient internally to retrieve the current user details. In the future, if we wanted to change the implementation of the service, we could do it without changing any web part code.

2) Calling the AadHttpClient from a custom service:


Similarly, we can also use service scopes to get hold of an instance of the AadHttpClient class. In the code below, to keep things simple, I am using the AadHttpClient to make a call to the Microsoft Graph. Eventually, the result is the same as the previous code but it should demo how to use the AadHttpClient through service scopes.


Consuming the custom service from an SPFx webpart:



3) Calling the SPHttpClient from a custom service:


And finally, if we just want to make a call to the SharePoint REST API from our SPFx code, we can also use service scopes to get hold of an instance of the SPHttpClient class. The following code also demonstrates how to get an instance of the PageContext class to get different run time context values like the current web url. Using the current web url, we can make a call to the SharePoint REST API to get the web details:


Consuming the custom service from an SPFx webpart:


And that's it for this post! I am planning more posts on this topic in the future so keep an eye out for those :)

As always, the code from this post is available on GitHub: https://github.com/vman/ServiceScopeTestBench

Monday, 4 March 2019

Create Azure DevOps pipelines for a Microsoft Teams app built with SPFx

In this post, let's walk-through the configuration needed to create Azure DevOps build and release pipelines for a Microsoft Teams solution. 

The solution is built using the SharePoint Framework and will be surfaced in Microsoft Teams as a Tab. 

As the code for the SPFx solution is being hosted in a GitHub repository, I will only focus on the Azure Pipelines configuration. For information on how to work with Azure Repos, see the official Microsoft docs: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/toolchain/implement-ci-cd-with-azure-devops

These are the high level steps we are going to follow:

Build pipeline:


1) Create production package of the SPFx solution (.sppkg)
2) Create a Teams manifest package (.zip) 
3) Publish the SPFx and Teams packages as Artefacts to be consumed from the Release pipeline

Release pipeline:


1) Deploy the SPFx package to the SharePoint Tenant App Catalog (using the Office 365 CLI)
2) Deploy the Teams manifest package to the Microsoft Teams App Catalog (using the Office 365 CLI)

So without further ado, here are the screenshots and scripts to achieve this:

(Click on the images to zoom)

Build pipeline:



Use Node 8.12.0:


npm install:


gulp bundle --ship:


gulp package-solution --ship:


The .sppkg file needs to be copied to staging directory in order to be published to the release pipeline:


Create a .zip file for the teams app catalog and copy it to the staging directory as well:


Publish the .sppkg and .zip files so that they can be consumed from the Release pipeline:



Release pipeline: 


Now to create a new Release Pipeline which will deploy the packages to the respective app catalogs in the tenants.

The Artefacts used will be the .sppkg file and the .zip file for Teams which was published in the previous step:


Overview of tasks in the Release pipeline:


Before moving on to the tasks, we also need to configure some variables to hold the information for the tenants, credentials etc.


The username and password should be for an account with global admin permissions. This is because the Microsoft Graph API (used by the Office 365 CLI) needs this permission to deploy apps to the Microsoft Teams App catalog:
https://docs.microsoft.com/en-us/graph/api/teamsapp-publish?view=graph-rest-1.0#permissions

The TeamsManifestId will be used to check if the app already exists in the Teams App catalog. This can be fetched from the manifest.json file in the teams folder of the SPFx solution:


If you are feeling adventurous, you could look at cracking open the .zip file from the Release pipeline and then grabbing the id on-the-fly instead of having to specify it in a variable.

Next, we will need to use Node (same task as the build pipeline) and install the Office 365 CLI on the Release agent using:

npm install -g office365-cli


Next, we need to deploy the SPFx package to the SharePoint Tenant App Catalog:


Here is the script as a gist:

And lastly, we need to publish the Teams manifest to the Microsoft Teams App Catalog. We will do this using the Office 365 CLI as well. Thanks to Elio and Waldek to get this functionality in the Office 365 CLI at lightning speed!


Here is the script as a gist:

And that's it! You now have build and release pipelines configured to deploy SPFx solutions and Teams apps. The webpart will now be available in SharePoint and the tab will be available in Teams:



 Hope you found this walk-through useful!

Monday, 11 February 2019

Debugging a Microsoft Teams Tab built with SharePoint Framework

With SPFx 1.7, the ability to build Microsoft Teams tabs with SharePoint Framework was released in preview. It's not just a SharePoint page hosted in a teams tab, it's aware of the context information around the current user, team, channel, tab etc. and is also able to interact with the team.

Here is the Microsoft docs article about it, have a read if you haven't already:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-web-part-as-ms-teams-tab

The article walks us through the process of building and publishing a production package of an SPFx solution so that it can be used as a SPFx webpart as well as a tab in Microsoft Teams.

In this post, we will go through the process of actually debugging the solution when it is being built. During development, when we make changes to the TypeScript code, we don't want to publish a production package to the App Catalog every time to test our changes.

The ideal development flow for me is to upload the SPFx package once to the App Catalog on the dev tenant, run gulp serve and when changes are made to the code, they should immediately be available to test as a Microsoft Teams tab.

To achieve this, first you will need to create an SPFx 1.7 solution as described in the docs, then update the webpart code to make sure that the microsoftTeams.Context is available to consume.

(Note: Make sure to use the SPFx v1.7.0 and not SPFx v1.7.1 which is the latest version at the time of writing this article. There is a bug in the latest version which does not create the teams folder automatically through the generator. More details here)

Now here is where the process deviates. Instead of creating the production package with the --ship flag, we will create a development package with:

gulp bundle

and then

gulp package-solution

After which you should see the console screen similar to this:


Getting the warning which says that the scripts (Client Site Assets) will not be packaged with the solution is important as we want them to be referenced from the local dev machine.

Next, we will upload and deploy the package to the tenant app catalog as usual:


Notice that the scripts will be loaded from localhost.

Next we need to make the scripts available from localhost. For that, we will run:

gulp serve



Now we need to upload the teams package which will make the SPFx webpart available in teams as a Tab.

This process is exactly similar to how it's done in the Microsoft docs article:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-web-part-as-ms-teams-tab#packaging-and-deploying-your-web-part-as-a-microsoft-teams-tab

We make sure that side loading of apps is enabled on Teams, then go to any Team > Ellipsis > Manage Team >  Apps > Upload a custom app > Upload the Zip file.

Then go to a channel in the Team > The plus button (+) > Add a tab > Select your app > Save

That's it! You Teams tab is now loading the scripts from localhost:


Make sure gulp serve is still running and then you can test by changing something in the code and press the reload tab button. The tab should be updated with the new code.

You can also test in the Teams Desktop client. Just make sure that the "Developer Preview" option is enabled:


Thanks to petkir for this tip!

After this, your SPFx tab should load in on the Teams Desktop client as well:


Hope this helps!

Wednesday, 2 January 2019

Working with Application Permissions (App-Only Auth) in SharePoint Online and the Microsoft Graph

When working with SharePoint Online or the Microsoft Graph, there are many scenarios in which we need to read or write data without a user context. It might be a scheduled process, or it might be an operation that requires elevated permissions. In such scenarios, it is quite common for the solution to use "Application permissions" (a.k.a App-Only Authentication). This lets the solution have its own identity which can be used to grant the required permissions.

When working with Application permissions in Office 365, there are a lot of moving pieces to deal with like Client Ids, Client Secrets, Azure AD App Registrations, Certificates, Add-In Registrations, AppRegNew.aspx, AppInv.aspx etc.

What I want to do in this post is to explore different options for configuring and granting application permissions. There are a few combinations possible with the different moving pieces. My aim in this post is to explore them and determine which combination might be suitable for certain scenarios. We will also see some sample code which demonstrates how to authenticate with SPO and the Microsoft Graph using the different authentication options.

Here is a table I have put together which summarises the different options for working with applications permissions in SPO and the Microsoft Graph API. We will go through each on them in detail.



1) Interact with data from SharePoint Online with an Azure AD App Registration


If your solution uses an Azure AD App Registration created from the Azure AD portal and you want to read or write data to SharePoint Online:

You will need to use a Client Id and Certificate. Have a look at this link for details on how to create an AAD App Registration as well as the certificate: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread

If you try to use a Client Id and Client Secret created through AAD portal you will get the following error:

Microsoft.SharePoint.Client.ServerUnauthorizedAccessException: 'Access denied. You do not have permission to perform this action or access this resource.'

You will also get the "Access Denied" error if you try to write to the User Profile service. Reading from the User Profile service will work. If your solution needs write User Profile service access, your only option would be to use an Add-In registration (see the next section). Writing to the SPO Taxonomy Service will not work either through AAD App Registration or Add-In Registration. Read operations will work. See notes at the end of this post.

Here is some sample code to demo how to use a Client Id and Certificate with the AAD App Registration. You will need the SharePointPnPCoreOnline NuGet package:


2) Interact with data from SharePoint Online with a SharePoint Add-In Registration:


If your solution uses a SharePoint Add-In Registration (created through the /_layouts/15/AppRegNew.aspx page) and you want to read/write data to SharePoint Online:

You will need a Client Id and Client Secret created through the /_layouts/15/AppRegNew.aspx page and permissions granted from the /_layouts/15/AppInv.aspx page

See this link for details on how to create as well as assign permissions to the Add-In Registration:
https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs

Here is a sample of how to use the Add-In registration to interact with data from SharePoint. You will need the SharePointPnPCoreOnline NuGet package.



3) Interact with data from the Microsoft Graph with an Azure AD App Registration


If your solution needs to interact with the Microsoft Graph, the only option is to have an Azure AD App Registration. However, within the Azure AD App Registration you can either use a Client Id, Client Secret pair or you can use the Client Id, Certificate pair as well.

1) Using a Client Id and Certificate:


The process to create the AAD App Registration and Certificate is exactly the same as described above in section 1. Only difference would be that instead of selecting SharePoint Online permissions, the App Registration will have to be granted the relevant permission to the Microsoft Graph.

Once that is done, here is the sample code to use the Client Id and Certificate to get data from the Microsoft Graph:



2) Using a Client Id and Client Secret:

The only change in this approach is using a Client Secret (Password) instead of a certificate. See this link to see how to generate a client secret for the AAD App Registration:
https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#get-application-id-and-authentication-key

Once you have the Client Id and Client Secret, you can use the sample code to get data from the Microsoft Graph:


In conclusion: 


Considering all factors, I would personally go with one of these two options:

If the solution is strictly going to deal with SharePoint Online data and not any other part of Office 365, you might want to consider the SharePoint Add-In Registration approach with a Client Id and Client Secret. That way you don't have to mess around with certificates. But remember that in the future if the same solution is going to read/write data from the Microsoft Graph, you might have to create another App Registration in Azure AD.

Another option would be to use an Azure AD App Registration with a Client Id and a Certificate. This allows us to interact with most of Office 365 data (including SharePoint Online and the Microsoft Graph) without maintaining separate applications. The caveats to this approach being the added complication of generating and managing certificates and also the fact that writing data to SharePoint Online Taxonomy and User Profile will not work (Reading data will be possible)

Notes:

1) Writing to the SPO Taxonomy Service with Application Permissions does not work from either AAD Portal or Add-In Registration. Read operations work. See more details here:
https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly#what-are-the-limitations-when-using-app-only

2) For the purpose of this post, I have only considered Azure AD v1.0 endpoint as we are only concerned with organisational accounts and not personal accounts.
https://docs.microsoft.com/en-gb/azure/active-directory/develop/azure-ad-endpoint-comparison

3) Technically, Add-In registrations created from the AppRegNew.aspx page are also registered in Azure AD. They are not visible through the AAD portal but you can list them via PowerShell.

4) It is also possible to create an App Registration in Azure AD and then use the AppInv.aspx page in SharePoint Online to assign it SharePoint specific permissions. You can also use this approach to assign a client secret which never expires to the Add-In registration. For more details on this, you can see this post by the very talented Sergei Sergeev https://spblog.net/post/2018/08/24/SharePoint-lifehacks-create-SharePoint-app-registration-with-client-secret-which-never-expires

It would be great if we can get some confirmation from Microsoft about this approach being supported/recommended. But even then, we will have to manage the SharePoint permissions in a different location than the Microsoft Graph permissions.

Hope you've found the post helpful!

Wednesday, 12 December 2018

Code Splitting in SharePoint Framework Part 2: Optimizing the SharePoint Starter Kit

I was having a look at the SharePoint Starter Kit recently and I have to say it's a very useful collection of sample SPFx webparts, extensions and other modern SharePoint building blocks. You should check it out if you haven't already.

While I was looking at the different components, I noticed something interesting: When I created a production package and observed the minified JavaScript files, the file-sizes were bigger than expected.

(Note that these are just the sizes when the files are extracted on the file system. When they are included in a package and loaded on SharePoint pages, they will be compressed so their sizes would be smaller. The image is just to help compare the file-sizes after the optimisations)


So I started having a closer look at the SPFx components and noticed a few interesting things:

1) @pnp/sp: 


As expected, many of the components were using the @pnp/sp package but each component was statically importing it. This meant that each component will have a copy of @pnp/sp in its individual bundle:

The solution was to implement code splitting and separate out @pnp/sp into it's own file:

This approach has two benefits:
  • All components share the same @pnp/sp bundle
  • The @pnp/sp code is loaded dynamically on the page only when required

2) @pnp/spfx-property-controls


The @pnp/spfx-property-controls package is great when it comes to having pre-created custom controls to use in the SPFx webpart property pane.

One thing worth noting though is that the property pane is loaded much less frequently than the webpart code itself. The property pane is used only to configure the webpart so the code is only needed then and not when the web part loads normally on the page.

To further optimize the webpart bundles, we can separate out the property pane code (including the components from the @pnp/spfx-property-controls package) and load it on the page dynamically only when the property pane is loaded.

So instead of statically importing the property pane components like this:

We could dynamically import them:

This would also mean that the property pane custom controls will be split into their own JavaScript bundles and multiple webparts using the same type of control will share the code:


This is particularly helpful with controls like `PropertyFieldCollectionData` which is more than 700kb in uncompressed format!

After implementing both these changes, we can see that the file sizes have been considerably reduced:


Also important to note is not only the filesize reduction, the main benefit of this approach is that there is no duplicate code in the components.

I have submitted a Pull Request with these changes to the SP Starter Kit GitHub repo if you want to checkout the code:
https://github.com/SharePoint/sp-starter-kit/pull/216

Here is a link if you want to checkout the official Microsoft docs on dynamic loading of packages:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/dynamic-loading

Thanks for reading!

Monday, 8 October 2018

Code Splitting in SharePoint Framework (SPFx)

Code splitting is not a new concept to TypeScript/React/Webpack developers. In short, it is a optimisation technique which allows us to split our application bundle into smaller bundles and load them on-demand only when required.

E.g. when a React component or a third party package is only needed when the user clicks on a certain button, then there is no need to load in on the first page load. It can be fetched on-demand when the button is pressed. This reduces the amount of data fetched over the wire on first page load, thus improving performance and user experience. This can be particularly helpful in large applications with many third party packages and components.

In this post let's have a look at how to do code splitting in the SharePoint Framework. As an example, I am going to use an SPFx web part created using React but the code splitting approach can be used with other frameworks/libraries as well.

We are going to have a look at two scenarios where code splitting can really help:

1) Loading a React Component on-demand (where we load the DetailsList component from Office UI Fabric)

2) Loading a third party package on-demand (where we load the infamous-for-its-large-size moment js)

So to begin with, here is my render method of a React component created by default by the SPFx yeoman generator:

I have edited it to show only 2 buttons. This component will be our "main" component which will load other components and third party packages when a user clicks on the relevant button.

Load a React Component on-demand: 


The _loadDocumentsClicked function will fire when the user clicks on the Load Documents button. The DetailsList component is defined in a file called DetailsListComponent.tsx which is in the same folder as the main component.

Once the import function fetches the DetailsList component class, we create an an instance of the class and use ReactDom to insert the component to the detailsContainer div in our main component. 

Load a third party package (moment js) on-demand:


Similarly, the _loadMomentClicked function will fire when the load moment js button is clicked. it will fetch the moment package and then assign the value of moment().calendar() to a property in the current component's state.

And here is the code in action on a modern SharePoint page:

(click to zoom)

What is also important to note is that the bundle will be loaded only if it was not loaded earlier. The import function is smart enough to determine if the bundle is already downloaded and it does not request it again.

Hope you found this useful!

As always, the code for this is available on GitHub: https://github.com/vman/SPFxCodeSplitting