Tag Archives: PowerApps

PowerApps Component Framework – Beyond the boundary

Published / by AK / 1 Comment on PowerApps Component Framework – Beyond the boundary

Note: I am going to write some dark magic with PCF. It is not currently supported by Microsoft. These are my personal findings. Views are my own. But if you want to dismantle PCF and want to test the boundary, then read further.

Credit: Andrew Ly for his Number Button Selector control and feedback. Rami Mounla for proofread and correction on the content.

The source code can be downloaded at my github.

Intro

Microsoft has announced public preview of PowerApps Component Framework (PCF) and its Command Line Interface (CLI) in April 2019. Many developers, including me, have been anticipating this new feature. I find the documentation useful. A few people has started building and sharing new controls. No disappointment from this beautiful community.

Absolute need

TypeScript. Although I am not absolutely convinced the preference of TypeScript to JavaScript (by the way, JavaScript is my favorite), it is the only choice we have right now. However, Andrew Ly has pointed out essential areas where TypeScript really shines, such as strong typing, OO concepts thus bringing the gap between JavaScript and OO languages like C#/Java.

Beyond absolute need

There is no doubt you can build any control following Microsoft documentation. But, I don’t want to stop here. I want to push its boundary and limits. So far, I recommend adding 3 more types of knowledge.

2. React for UI components

When the sample from Microsoft documentation shows building the UI using TypeScript, it reminds me of my student days when I had an assignment task asking to build a UI in C++. I could not force myself to create UI in C++ program.

TypeScript for building modern UI? No, please.

I admit I was super lazy during my student life. I only wanted to deliver bare minimum. So, UI in code file is a NO for me. However, as my career began and I collected some experience, I allegedly become a bit wiser. Yet, the wiser me supports the lazy me.

There are many libraries to choose. However, you can find react and react-dom packges under node_modules after executing “npm install” at step 4.. So, React is the choice. You can also use JSX which makes life easier when building DOMs.

2. NPM for packages

The motto of “Why reinvent the wheel?”. NPM hosts thousands of free packages. Most of the well-established libraries are now distributing over NPM. It is like NuGet for JavaScript. Now, you may ask “why not reference the CDN?”. Well, that brings me to next one.

3. Webpack for bundling

A module bundler. It will bundle all your resources, and packages from NPM, as a single package. Referring to CDN will only work when there is connectivity (or it is dependency). If we want to use it offline in mobile devices, we may encounter some issues.

The good news is that you don’t need to worry about NPM and webpack. PCF CLI handles them gracefully in most cases. I did however notice that there are some issues with adding large NPM packages (>500kb). The bundling tends to behave weirdly.

Create a new PCF Project

You use “pac pcf init” command to create a new PCF project. It will pre-create ControlManifest.Input.xml and index.ts.

pac pcf init
pac pcf init

Create a react component

Let’s create DemoComponent.tsx and write some scripts. The sample source can be found at my github repository.

Project structure
Project structure

Run ‘npm run build’ command. You will get an error saying ‘–jsx’ is not set. To resolve this, you need to add “jsx”: “react” in tsconfig.json located under your root folder. After this, you will be able to run ‘npm run build’ command without any issue.

JSX error
JSX error
Update tsconfig.json
Updating tsconfig.json

Now, let’s render DemoControl in index.ts. You will see an error and you will not be able to run ‘npm run build’ command.

Render react component

Now, let’s change index.ts to index.tsx and you will see the error is gone from IDE. You also need to update the code file extension in ControlManifest.Input.xml.

But, when you run ‘npm run build’, you run into another issue saying ‘Code file needs to be a typescript (.ts)’.

Looking at the error, it comes from pcf-scripts module which is located under node_modules folder in your PCF project.

tsx error during build
tsx error

A quick search using the error message brings me to line 84 at controlcontext.js under pcf-scripts module.

Include tsx
Only allow .ts

You simply need to update ‘if’ statement to allow .tsx extension and save it. And Magic happens.

Include tsx
Add .tsx into file type

Now, if you run ‘npm run build’, it will succeed. Next, it’s time to execute ‘npm start’. When the browser is launched, you will see the error ‘React’ is not defined.

React error
React error

To address this error, open index.html under your pcf root folder\node_modules\pcf-start and add following two script tags.

<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

As soon as you save your changes, your react component will be rendered.

React control demo

Congratulations! You have created a custom control using PCF and React.

The proceder in this article is experimental only.

Final word

Even though you can deploy React control to model-driven app, this approach is not currently supported by Microsoft. I am sharing this only for experimental purposes and showing how PCF can be extended using modern libraries. I am not a pro-React developer nor a TypeScript developer, therefore, I may have approached this the wrong way. If you have any comments, please share them below.

Six months after deploying my first PowerApps

Published / by AK / Leave a Comment

This post is more of rant than evangelist.

In October 2018, I had involuntarily involved in developing PowerApps for one of our clients at work. “Why involuntarily?” you may ask. Well, although I mainly work with Microsoft platform, I play around with iOS and Android native app developments and help out my friends. So, I fairly know the biggest challenge with mobile app development, making your app to work across different devices.

PowerApps is great. I have no doubt about it. All canvas-apps run inside PowerApps native app on your mobile, thus making it very different from cross-platform development in mobile app. It also means you have no control over the release of PowerApps native app.

Regardless of my strong objection, we managed to deliver the app in 10 days. The purpose of the app is to transform their paperwork of auditing farms. Basically, admin will create audit records in D365 and assign to auditors, then auditors will download records to their apps for offline use, drive out to remote areas for auditing (which also captures photo and signature). When auditors arrive back to their home at night, they sync their offline data to D365 and get newly assigned audits. Photos need to upload to their Google Drive. Why not? Microsoft Flow has out-of-the-box connector to Google Drive. Easy peasy.

Then, the client wanted to add a new functionality which will share photos uploaded in Google Drive to the audited farm. Although you can share files/folders in Google Drive, out-of-the-box connector is not capable of doing it. After going through Google Drive REST APIs, we solved it by creating a custom connector. To deliver all these functions in 10 days was impressive. It was working fine. Well … for two months.

In December, the client told us some of their auditors were facing issues with syncing records. We enhanced our app for a better performance. During the enhancement, it was all fine. When the app was re-opened again, it showed over 200 errors on one of global variables in the app. Suddenly! Set and Patch function on record type global variable didn’t work anymore. (You can find the similar issue here, though I didn’t report it).

I ended up with re-creating an app almost from scratch again. Tweak and fix. Again and again. One of the challenges with web player is you can’t test offline mode. SaveData and LoadData doesn’t work. I hope Microsoft will at least create stubs for these two functions for web player in the future. Having to test it on the device, you have to publish the app, and open the app at least twice in the device to see the latest version. It is not productive at all, but we managed to fix all issues.

But, there was another problem. Microsoft has released the new version of PowerApps and unfortunately it was a bad version. Without noticing the bad version, I kept working on the app. After publishing to test it on my device, it was too late. There was no way to return. If I rolled back to the working version, all my changes will be lost. If I stick to the current version, it can’t be tested. It didn’t look good at all.

Luckily, Microsoft released the bug fix over the weekend and the original issue was gone. But, again, the app was starting to behave weird. Functionalities used to work were not working anymore. For example, if I rolled back to the old working version, it worked. But, not on the latest PowerApps version. It is so frustrating for a developer. You have no control over which PowerApps runtime you want to choose. Microsoft dictates it. Because of it, one bad runtime version can create many issues even if you don’t make any changes.

During the enhancement, we started to notice errors in listing Google Drive files in Microsoft Flow. After searching Google Drive REST APIs, it turns out Google limits items returned from API to 100 by default and it can be changed. However, there is no way to define the paging size in out-of-the-box connector. We didn’t have the issue in UAT and early days of production go-live since items were less than 100. But, now we have an issue. As usual, a custom connector saved our life.

All issues seem to be sorted out, at least, for now. Hopefully, the app will behave nicely for next two months. But, I learned following lessons.

  • PowerApps is easy but not necessarily simple – a lot of gotcha and similar functions with totally different behaviours.
  • A lack of ability to control PowerApps version – one bad version will break your app into pieces just by publishing without making any changes. If it happens in development phase, it will stale the timeline.
  • Out-of-the-box connectors are useful for demo but not reliable for production – always reference to the source API documentation, not Microsoft Flow documentation.
  • Community is great but not all issues are resolved – looking at the example with Set and Patch functions in above link, it took about 2 months to recognise the issue by Microsoft PowerApps team. The worst part is it is not resolved yet.

Creating a custom connector to upload a file to Google Drive from PowerApps – Part 2

Published / by AK / 1 Comment on Creating a custom connector to upload a file to Google Drive from PowerApps – Part 2

Part 1 can be found here.

Swagger time

To create a customer connector, you can upload either a swagger file or a Postman collection. I am not a swagger expert. But, apistudio makes my life easier. Let’s jump onto http://specgen.apistudio.io.

Paste the URL that you used in Postman. Make sure you are using POST. You should receive 202 status after sending the request. One more thing to remember is to click ‘Next Step‘ button instead of clicking tabs. Otherwise, you will need to rework.

Generate swagger

Generate swagger

Enter API Program and contact info. Ensure to use valid Contact Url and Contact Email. What’s next? Next Step button.

Swagger contact info

Swagger contact info

Next is API Info. You can play around with the slider which will change API Base Path and API Path. Give a meaningful OperationId.

API Info

API Info

There is nothing special to do with Headers Info and Params Info, although we will make some manual changes in them. I don’t see (and I don’t know) to make those changes in the generator.

Keep clicking Next Step until you get Open API Spec. There you can download the swagger file.

Open API Spec

Open API Spec

Now, let’s make a few changes to swagger file.

First, we need to replace null with application/json in “produces” collection, and remove the whole section for “content-type”

Remove content-type

Remove content-type

Modify produces

Modify produces

Set default values for api-versionspsv and sig. You can find these values in URL or using Postman.

Set default values

Set default values

Lastly, you need to add parameters for file content, which will be reading from formData as a file type.

Adding file params

Adding file params

That’s pretty much you need to do in Swagger file before creating a custom connector in PowerApps.

Creating a custom connector

Launch PowerApps in the browser and go to Custom connectors, and import your swagger file.

Create a new custom connector

Create a new custom connector

Create custom connector

Create custom connector

Generally, you don’t need to change anything until you get to 3. Definition. At 2. Security, you can still use ‘No authentication’ as the API will authenticate using sig parameter you have provided in the query string. As long as you don’t expose signature parameter, no authorised user can use your API.

At step 3, scroll to Request section where you will see query parameters. Edit api-versionspsv and sig, and change their visibility to internal since we have set default values in swagger file.

Modify visibility

Modify visibility

After changing visibility of api-versionspsv and sig, you can now create a custom connector.

Once it is created, it is time to head into PowerApps. In your PowerApps, while adding a new data source, you can now find your custom connector.

Custom connector in PowerApps

Custom connector in PowerApps

After creating a data source, let’s add some controls to PowerApps and check whether it’s working. I have added PenInput and Button as below. OnSelect of Button, I am going to push the image from PenInput to my Google Drive.

Sample PowerApps

Sample PowerApps

Viola. It is now uploaded to Google Drive.

PenInput to Google Drive

PenInput to Google Drive

PenInput to Google Drive

PowerApps does not work

You know it right? Save, close and open your PowerApps again.

One last thing

This is only an example of how we can easily (of course, I can know say ‘it is easy’) transform any Flow functionalities as custom connectors. This also solves the concern of using Camera control which produces a low resolution photo. With this approach, we can use AddPicture control not only to take full advantage of device camera but also to pick up photos/files which are already existed in the device.

Generally, we don’t need custom connectors if parameter types are simple. In this example, we need Base64. I guess, depending on the parameter type of an endpoint, PowerApps may be passing suitable parameters.

Anyway, we can now invoke all those infinite possible connectors of Flow from PowerApps.

 

Creating a custom connector to upload a file to Google Drive from PowerApps – Part 1

Published / by AK / 1 Comment on Creating a custom connector to upload a file to Google Drive from PowerApps – Part 1

Knowing the limitation

Although Microsoft Flow and PowerApps support ready-to-use connectors for different services, it seems they do not have the same level of offerings between them.

In Flow, for instance, there is a connector for Google Drive and there is a bunch of actions like creating/deleting a file, browsing the folder.

Google Drive Actions in Flow

Google Drive Actions in Flow

However, you cannot use those actions in PowerApps. Apparently, Google Drive connector in PowerApps only works for excel as a datasource.

Google Drive in PowerApps

Google Drive in PowerApps

Additionally, if you pay attention to different image controls, such as PenInput, Camera, Add Media, they have different properties which looks like they are doing the same thing. For example, Camera.Photo returns a Data URI of a picture, while PenInput.Image and AddMediaButton.Media effectively returns a local blob storage URL (appres://blobmanager/<GUID>/<serial number>) which can only be accessed in the app.

Combining with the inability of using full functionality of connectors and meaningless local blob storage URL outside the app, we need look at what else we can do.

Knowing the out-of-box capability

Given that Camera.Photo returns a Data URI string of an image, we can convert it into binary and save it into Google Drive. However, it has another small issue. Camera control in PowerApps does not use the full resolution of device camera. It is ridiculously small (at least to me). If even we can live with small photos, we still need a workaround for other controls.

Out-of-the-box, you can upload images from above control to Azure Blob Storage. Then, from Azure Blob Storage, you can copy the file to the destination you want. This is not a problem for a client who has already had Azure subscription, but not a neat solution for a client who has no prior Azure subscription. Trust me the cost is never a problem (Azure Blob Storage is super cheap) but it’s more about managing different instances. If managing different instances is not an issue, this is a way to go.

Going an extra mile

This is where a real fun starts.

But, we always want to go an extra mile to see what else there. In the bright side, PowerApps allows us to create a custom connector which can be configured in Flow to upload a file to Google Drive.

First, let’s start with creating a flow which will be triggered when HTTP request is received. There is only one trigger available. This is basically a webhook.

HTTP Request Trigger

HTTP Request Trigger

HTTP Request Trigger

HTTP Request Trigger

Then, initialise the ‘FileName’ variable, so we can save the flow. The expression for value is trigger()[‘outputs’][‘queries’][‘filename’] as we are planning to pass query string filename during the request.

Initialise FileName variable

Initialise FileName variable

After saving the flow, expand the HTTP trigger point to grab the URL to invoke

URL to invoke

URL to invoke

Copy that URL to Postman, and append filename query string in URL. Please note sig query string should be kept in secret.

Test from Postman

Test from Postman

After invoking the URL, you will see the Flow is executed. When you open the execute Flow instance, you will see the filename query string is assigned to FileName variable in Flow.

Test Result

Test Result

Now, let’s try sending the file from Postman and let’s look at the Flow to extract file content. In your Postman, choose File form-data and send the request.

Send the file from Postman

Send the file from Postman

Open the executed flow. This time, you will see ‘click to download’ in the output of HTTP request.

Test Result with file

Test Result with file

When you open it, you will see a massive output, but we are only after for a few things which are body and $content. $content is Base64 of uploaded file.

Multipart body content

Multipart body content

To get this content, add another variable initialiser. This time we are going to set its type as Object, as Google Drive accepts JSON.

Prepare file content

Prepare file content

Now, let’s add another action to upload a file to Google Drive. It will ask you to connect to Google if you have no prior connection.

Google Drive connector

Google Drive connector

Now, let’s choose the folder path in Google Drive, file name and its content. You will need to write an expression for file content to get the object of FileContent variable.

Google Create File action

Google Create File action

Save the Flow. Go back to Postman, and send the request. You will see the flow is executed and a file is uploaded to your Google Drive.

I will cover the process of converting this Flow into a custom connector and calling it directly from PowerApps, in Part 2.