Displaying high quality photos in PowerApps leveraging the SharePoint Graph APIs

It has been nearly 6 months since I posted a blog on SharePoint Images Powered Up In PowerApps wherein I promised that there would likely be a series of blogs to follow as I had only touched the surface with regards to what potentially could be further accomplished with the SharePoint RenderListDataAsStream REST API.

However Ashley & Paul were right on top of it, and just a few days later posted a great blog, leaving me far behind in the process ?. Check it out if you haven’t already:
Some seriously powerful (and free) PDF functionality in PowerApps and Flow

As Paul states in their blog though, if only we got $250 or so for every 5 or so steps we often code; and I’ll add my 2 cents to that “from each person who copies or reworks those 5 steps for their own apps”.  

Sadly for us at least it never actually pans out that way! Hence why subsequent to the initial blog on this topic I posted in June, I am still about $250 short of the 1st $250 payment I was hoping for. I gave up hoping to get rich, so a couple months ago I shared the Flow as well as the 2 Power Apps I had showcased in that blog on GitHub in the hopes that in doing so, others might perhaps at least save $250 or so of their time and money better spent elsewhere.

In my previous blog I included a section: 
“Why not then simply use Microsoft Graph?
wherein I detailed reasons why, at that time at least, it wasn’t viable to leverage Graph APIs in apps created using PowerApps and Flow. I thankfully left the door open in adding a disclaimer or two to cover myself on that ?:

If Microsoft Graph API REST calls perhaps supported passing the authentication token…”

To obtain an access token you would thus in any case require a flow to return the access token each time the PowerApps app was launched, and if necessary, should the initial access token expire within a PowerApps session, call that Flow once again to obtain a new valid access token.

Microsoft listened, and a few days ago I became aware of a recent blog post by Chaks who leads the PowerApps and Flow experiences from the SharePoint team within Microsoft.
Displaying file thumbnails from a SharePoint library in PowerApps

The by-line on Chaks’ Corner web site landing page for his recent blog reads:

“Things should be made as simple as possible, but not any simpler.”

After giving Chaks’ blog a quick once-over read, I decided to accept the challenge to see if I could make that code somewhat simpler ?. Alternatively perhaps easier to understand by way of example by reworking the demo apps I had showcased in the initial blog in the series, only this time leveraging the Graph APIs to generate URLs instead of consuming the somewhat similar URLs generated when calling the SharePoint RenderListDataAsStream API.

Introduction

The primary purpose of this blog is in fact to debunk the myth that the alternate technique per the initial blog I posted leveraging the RenderListDataAsStream API was the only viable way to consume particular types of content (e.g. documents, images etc.) from SharePoint in your PowerApps, whilst equally displaying that content (especially images) as highly compressed but nonetheless exceptional high quality rendition(s) of the original images / photos as stored in your SharePoint document libraries.

In a nutshell you can and more importantly you should in fact use the Graph APIs with apps you build using PowerApps and Flow as Microsoft deem consuming the URLs generated by the Graph APIs as the recommended best practice.

There are a number of sound reasons why leveraging the Graph APIs is considered a far better technique as opposed to using the RenderListDataAsStream API is, regardless if seemingly on the surface at least both patterns may yield the same desired results, depending on your requirements.

Hopefully by the time you’ve worked through this blog you will understand some of the more technical reasons as they speak to fundamental differences between the 2 techniques, and why of the 2 techniques the “Microsoft” recommended practice is to leverage the Graph APIs.

Demo – Photo App

Before going into further details let’s look at a demo of v2 of one of the sample apps I showcased in my previous blog – i.e. the same functionality, however this time primarily leveraging the GraphAPIs to generate authenticated URLs for the images displayed.

Platforms and Services Leveraged

Office 365 Services (Apps) Consumed

  • Power Apps
  • SharePoint
  • Power Automate
  • Graph APIs

As it was with the demo apps in my previous blog, the demo apps and overall solution as is described in this blog is equally based upon entirely OOB functionality available today within the Office 365 platform.

Premium Connectors / Third-Party Applications Required

  • N/A

Complexity

The code detailed in this blog that leverages the Graph APIs is, relatively speaking, very similar in terms of complexity when compared to code per the initial blog I wrote that leveraged the SharePointRenderListDataAsStream REST API.

There is one subtle visual but important difference between v2 of the demo app and the v1 version. In the v2 version I display the original size of each photo. On the surface the original size may be of little relevance as with both the Graph API and the RenderListDataAsStream API the images rendered are almost certainly equally highly compressed renditions, given that both APIs return Azure Media Services (AMS) Transform API URLs. However, with the Graph API you can expose other metadata that is not exposed when solely leveraging the RenderListDataAsStream API.

Thus, by example, in v2 of the app the original size of each image is displayed whilst in v1 of the app this would have unlikely been possible. 

Fact or Myth – Why not then simply use Microsoft Graph?

Given that evidently it is in fact now possible to render images in PowerApps leveraging a Flow to generate URLs from a Graph API based on v2.0 of the said APIs, it is now worth revisiting the assumptions I at least called out in my previous blog in this series and explore to what extent each of those is now at least fact or myth.

Access Token & Graph API URLs

With v1.0 of the Graph APIs there was no way to pass any sort of authentication token in the http request to any of the Graph APIs unless you first registered an app in Azure in order to acquire an access token. With v2.0 of at least some of the updated Graph APIs this is no longer the case, and a tempauth access token is generated when you request the thumbnails of files (aka DriveItems) from a particular document library (aka Drive).

Given the same query used in v2.0 of the Graph API as is described on Chaks’ blog (Get items) fails should you instead call v1.0 of the API, it is my understanding at least that leveraging this technique with the Graph APIs would not have been possible when I published the first blog in this series in June 2018.

Of particular relevance in terms of key difference as they pertain to the access token generated when calling the RenderListDataAsStream API vs the tempauth access token generated using the Graph APIs is that URLs generated when calling the Graph APIs are short-lived and are specific to each thumbnail URL generated whereas the access token generated by the RenderListDataAsStream is valid for far longer.

Of greater significance, the same access token returned by the RenderListDataAsStream API could potentially be used to access any content stored in SharePoint unintentionally should a user, for example, unknowingly share a URL generated using that API with other users, and in doing so possibly comprise what could otherwise be deemed sensitive content they have access to elsewhere in SPO until such a time as that access token expires. 

In summary, the historical limitation whereby the Graph APIs did not support including some type of authentication token within calls to the Graph APIs can be declared Myth, going forward at least with the v2.0 release.

Highly inefficient

Given that with v2.0 of the Graph APIs the URLs generated by the Graph API for thumbnail images equally in turn leverages the AMS Transform API, there should by all accounts be no difference in terms of the highly efficient and optimised compression of images when compared with using the RenderListDataAsStream API technique. Hence this equally can be declared Myth, again going forward with the v2.0 release of the Graph API at least.

Cumbersome to Code

To quote from Chaks’ blog, rather than using my own words:

Note that this is an advanced tutorial and requires good knowledge and understanding of how Flow and PowerApps work along with calling Microsoft Graph APIs.

It terms of the complexity of this specific point as it at least pertain to the context which I was speaking to in my previous blog, I am going to go out on a limb and refer back to the Chaks’ by-line on his web site:
Things should be made as simple as possible, but not any simpler.

As it stands now, this point I raised in my previous blog is clearly Fact. However with time, more individuals will blog on the broader topic of consuming content exposed by the Graph APIs in PowerApps and Flow scenarios, and accordingly in time far more complex reusable patterns will emerge and equally be shared to the broader PowerApps App Makers community.

This is notwithstanding that given the latest evolution of the Graph API that now enables this particular technique to become feasible for the PowerApps team to incorporate rendering images in this way as a native capability in the product most importantly leverages a technique that is now become a Microsoft recommend practice.

The core reason for Microsoft now promoting this technique as a recommended practice is because content exposed in leveraging v2.0 of the Graph API is now sufficiently secure whereas with the RenderListDataAsStream pattern per my previous blog, content could inadvertently be shared with an unintended audience. 

Put differently, the PowerApps product group are no longer constrained by the limitations of v1.0 of the Graph APIs, and even this blog should be considered as an interim measure that App Makers can leverage in way Microsoft would far rather have their customers use whilst this same technique can in time be introduced as native functionality in what I certainly expect is in accordance with a well-defined roadmap. Watch this space…

And Now… The Solution v2.0

Given that some of the historical limitations of leveraging the Graph APIs within your PowerApps to render content is no longer necessarily a constraint for App Makers, let’s take a look at how I constructed the demo app for this blog. 

Power Automate Flow Component of the Solution

In terms of creating the flow used in this demo, I followed the guidance Chaks shared in his blog. As Chaks went into great detail in his blog, in this blog, I would rather not speak too much on how this all works, especially given I am sharing a link to the source code for the entire demo app and flow I created.

Notable Challenges I Encountered

Whilst building the Flow I use in my Demo app, I did experience a few challenges worth calling out based of Chaks blog. Accordingly should you follow his blog to the T and wonder why it might not work for your own document libraries, here are a few pointers:

  • By default the call to the Graph Drive API does not return system facets such as the ‘Site Assets‘ Document Library, ‘Site Pages‘ Library etc. In my demo app my photos were stored in the ‘Site Assets‘ library. Rather than move those files to a standard document library I decided to rather amend that particular API call so others can have a functional example of how to include system libraries when calling that API. 
  • Chaks notes in his blog that it is not possible to get the Drive URL of a specific library using the Graph Drive API. Instead a list of all Drives (Document Libraries) are returned in the “SPDrives” step. The next step, “SPDrivePictures“, calls the Filter Array function in Flow to refine the entire list of document libraries returned from the SPDrives to the specific library containing the images to display in your app. 

    In testing however, I found that when I added the next step thereafter, “Get item” the flow still considered the value returned by the “SPDrives” step to be an array and then kept insisting the “Get items” call to be in a ‘For each‘ loop in flow. To resolve that I added a couple of steps between to get the first row in the filtered array, albeit that the filtered array should in any case always return at most one record.

Demo App – Build Flow

As I noted above, Chaks has already done a great job documenting how to create the flow, and as such I would rather not go into extensive detail on how to define each step in Flow.

There are two steps that I modified that perhaps warrant a couple of notes:

In the “Get list items‘ step I removed the Title field from the query and added size in order to display the file size within the demo app. The reason I removed the Title was due an error I kept getting in flow because the majority of the photos do not have the Title field populated. There may be a way to check for the presence of a property of an item, such as the contains expression in Flow, however equally per the comment on this thread dated December 10, 2018 the contains function didn’t work for me either.

In the 2nd last step in the flow for the demo solution, I made a couple of small but perhaps important changes to this as they relate to Chaks’ blog and the flow he shared.

I decided to add a return field “ContentType” to the array returned to PowerApps. This property / field allows you to use a Filter in your Gallery control in PowerApps to either display only folders items or alternatively exclude folder items entirely.

The other change I made was to the formula of the Thumbnail property:
“if(empty(item()[‘thumbnails’]),”, replace(item()[‘thumbnails’][0][‘large’][‘url’], ‘width=800&height=800’, ‘width={width}’ ))”

Should you use Chaks’ flow as-is, if the library you are querying contains any folders, the flow will fail as there is no thumbnail (currently) associated with the folder content types. I instead check for the presence of thumbnails for all items, and only if the item has an associated thumbnail do I actually then obtain the URL.

What I have also done for the demo solution is Replace the default width & height properties of each large thumbnail URL returned [800×800] with a placeholder {width} instead, following which in my demo app I use the Substitute function such that I am actually in no way limited to a particular size. In essence the size of thumbnail requested is thus irrelevant!

So where’s the source code dammit!

I am gonna do away with any further technical details and simply share the source code for ya all. 

GitHub – Leveraging Graph APIs in PowerApps and Flow


Demo – Same App with Documents

[Edit] With the same app and overall solution showcased, you can equally alternatively view ‘thumbnail’ images of not just images but documents as well. Per the demo below, the same Flow is called, this time passing the default site document library (‘Documents‘) as a parameter to the Flow.