What are Low-Code development platforms?

Rapid Application Development, Robotic Process Automation, Business Process Management Systems – these are all examples of Low-Code platforms. The market has already appeared in the 90s, but their popularity has grown strongly in the last few years. In this article, you’ll learn what the Low-Code platforms are, when you need them, and what future is waiting for them on the example of Microsoft platforms like PowerApps and Microsoft Flow.

Thorough transformation – Understanding ” The Why”

Before we get to know what the Low-Code platforms are, we first need to understand the reasons why we started to work on their development at all. For this purpose, let’s get back to the 90s. At the time, it was the era of computerization that got under way for good. E-commerce, e-government, e-society, e-everything has dominated the world as personal as well as business. It became obvious that there is no other way than to give IT a high priority in computerization of work and activities. Data Management, Information Management and Knowledge Management were gradually digitized, and business processes were transformed along with them. All this so that everyone can use private resources, corporate resources, and communicate with others from anywhere in the world and at any time of the day or night.

Source

Where to get new programmers from?

Such a change naturally required the involvement of a huge number of IT specialists and engineers. IT specializations have become extremely popular and well rewarded. Everyone did what they could to increase the number of programmers:

  • Educate new developers – IT courses in higher education were gaining in popularity. Currently, in Poland, as reported by the Ministry of Science and Higher Education, 14% of all recruitment applications for 2017/2018 concerned information technology, the second was management with a result of 7% of all applications!
  • Employees of other specializations were attracted – all-year programming schools, bootcamps and IT courses are doing everything to further reduce the training time of the “complete junior”. Also the employers help in this process eg Aviva, the insurance company, in 2018 organized a 6-month course addressed to all its employees, giving them a chance to get software developer qualifications
  • Experienced developers are tempt – hundreds of recruitment companies flood with offers anyone who on linkedIn has anything to do with IT. They’re offering many benefits besides very good earnings
  • Promote awareness 😉

Despite almost 30 years, the situation has not changed. Reports estimated that in 2020, 500,000-600,000 programmers will be missing out on the European market (source, source)! Only in Poland, my homeland, there is already missing about 50,000 of developers. Demand around the world is huge and Europe Union estimates that it will last until 2030 (source). But it is not surprising. More and more industries involve IT solutions (HR, marketing, art) and the same is for areas of activity (forecasting, chatbots).

Image result for meme when a developer uploads cv

Low-code platforms for the rescue

If the demand is greater than the supply, there are two strategies to keep the balance. Increasing the supply (that is increasing “the production” of developers) has been described above. However, method no. 2 consists in reducing demand for programmers. Is it possible? It turns out that yes and no need to blow up in the air half of the companies ;). It is enough to build a platform for effective, fast and flexible building of applications and processes. The platform itself has to fulfill 5 assumptions:

  1. Ease of use – everyone, not only highly qualified engineers, should be able to use it
  2. Supporting standards – basing the platform’s operation on globally accepted and adopted standards
  3. Integration – the platform should enable integration with any systems. Achieving point 2 is very helpful in this 😉
  4. Accessibility – building solutions and using them should be independent of a user OS (Windows, Linux, macOS)
  5. Extensibility – a platform that can not be easily expanded has little chance of being longer on the market

And that’s what the so-called Low-code development platform [LCDP] – a visual approach to application development. Microsoft has at least 3 such platforms. In the further part of the article I will focus on two of them: PowerApps and Microsoft Flow. They co-exist in a very close relationship not only among themselves, but also with more than 230 other platforms (Microsoft and non-Microsoft).

Examples of Low Code platforms by the Microsoft. Microsoft Flow and Azure Logic Apps are very similar, although they differ in their purpose: Microsoft Flow is mainly used for business processes. Azure Logic Apps is responsible for the integration of data between different systems. I will deal with them in another article.

At LCDP, we build solutions using a drag-and-drop or point-and-click mechanism from the graphical interface. Thanks to this, building, modifying and maintaining it is extremely simple and saves a lot of time, and the effects are visible immediately.

Microsoft Flow example
Microsoft PowerApps example

Using LCDP is a bit like building with LEGO bricks. When you play with LEGO, you use manufactured blocks that are limited in size, color and number of pins. However, these dependencies do not limit the construction of complex and functional solutions.

With the use of LCDP you can build professional solutions supporting the company and what is important, the construction process itself is fast and agile, saving time and money.

Check out the application to scan business cards, which development took me 8 hours instead of 5 days!

This approach must give effects:

1. 70% less application development cost and effort
2. 362% return on investment over 3-year term
3. <3 months payback

Data from “The Total Economic Impact of PowerApps and Microsoft Flow” published by Forrester Consulting in June 2018 (source)

Low-Code, High-Possibilities

The possibilities of the platform itself are determined not only by the multitude of available “blocks” from which we can build a solution, but also the openness of the platform for integration. The PowerApps and Microsoft Flow platforms share a set of available connectors that are over 230!

One of such connectors are those related to HTTP query support which actually is synonymous with the ability to integrate the platform with any SaaS solution having a REST API (so the overwhelming majority).

Left: Http trigger which launches Flow when it receives an HTTP query from any source
Right: HTTP action that performs an HTTP query to any URL
(If you’re a developer, know that creating your own SaaS service could not be easier!)

But it is not everything. Microsoft Flow, like PowerApps or LogicApps, allows you to build your own “plug-in” (so-called custom connector). Developers can connect such Custom Connector, for example, with Azure Function, which can perform ANY operations using the C # code, Javascript, Python, etc. A good example is the application for document translation, which consists in:

  • Microsoft Flow sends a document to Azure Function
  • Azure Function decompose the document to separate paragraphs…
  • …and translate each paragraph independently.
  • Then the function merges together translated paragraphs…
  • … and sent back to Microsoft Flow

Thanks to this approach, the document is translated in full, maintaining the formatting and layout of information on the page.

Use the resource according to your the needs

The scenario presented above has one more extraordinary advantage. This approach is definitely beneficial for more than 1 page of the software development process:

  • LCDP developer – Can model the overall flow of the entire process. Determines from which platform the user interacts with the application (eg PowerApps), where the data is stored (eg SharePoint Online) and how the general flow of information related to the translation of the document is going (eg Microsoft Flow). However, he does not know (and does not need to know) exactly how the document is translated
  • Software developer – With the help of a regular code (eg C #) he creates an independent service that gets the document in a language at the input and translates it to any other, returning the translated document at the output. For this purpose, he can use services like Azure Function and Azure Cognitive Services. He does not know (and does not need to know) what is happening with the document before and after the translation process.
  • Business owner – he uses his resources according to their purpose, thanks to which fewer mistakes are made and more value is delivered in a shorter time. People who understand business well and have broad product knowledge can focus on modeling the overall framework of the solution. In turn, programmers can focus on this part of the solution that makes the best use of their specialization.

This is very important: using LCDP you do not give up the possibility of using solutions using a regular code.

LCDP and the vision of tomorrow

The last question is: is it worth letting LCDP platforms into your organization? There are some questions related to that:

  1. The LCDP concept is not new and it was already known in the 1990s. Why, then, have they become popular yet? Maybe it’s just a temporary hype?
  2. Will LCDP platforms be replaced soon by other platforms?
  3. When choosing a single supplier, do not we enter a one-way street?

So let me answer these questions briefly:

  1. That’s true, LCDPs have appeared before, however in my personal opinion their low popularity was associated with the lack of appropriate adaptation of programming standards (REST / GRAPH API, JSON, OAUTH). Such lack of adaptation made it impossible to build a wide range of integrated platforms. At present, the IT environment has clarified such universal standards, and newly-built solutions follow the established good practices. There is one more important reason, more precisely 3 of them:
    • Growth – it is estimated that the value of the low-code market will increase by over 21 million over the next 5 years
    • Diversification – the demand for IT solutions is growing, and at the same time software developers need a relief
    • Integration – solutions need to grow quickly while maintaining integration to be able to use the potential behind AI, robotics and machine learning
  2. In the whole history of IT solutions, a lot of products appeared and some time after sold, changed or killed. Therefore, I personally think that it is best to relate to solutions of large companies whose future is based on the trust of their clients. This trust depends directly on the possessed solutions, as well as their stability, predictability, reliability and security of entrusted data. “Trust is the Currency of the Future” as the title of an excellent Polish book by Michał Szafrański.
  3. Yes and no. On the one hand, actually choosing ANY supplier from the exit procedure, it may be even necessary to rewrite the solution in case of willingness to change. On the other hand, the construction of the graphical interface should not take much time, and in the case of RPA systems, you can implement critical parts of solutions as independent SaaS solutions (eg using Azure Functions or azure hosted web applications)

Conclusion

For me personally, Low-Code development platforms are a must for any company that wants to grow dynamically. They carry a huge revolution that once brought a window interface in operating systems. On the one hand, it’s just a change of interaction with the processor “on the other side.” But on the other hand, it is opening up to completely new possibilities.

If you would like to learn more about these platforms contact me! I will teach you how to use them, and if you want I can design a solution or simply build it for you!

Czym są platformy Low-Code?

Rapid Application Development, Robotic Process Automation, Business Process Management Systems – to wszystko przykłady platform Low-Code. Na rynku pojawiły się już w latach 90tych, ale ich popularność wzrosła silnie w ciągu ostatnich paru lat. W tym artykule dowiesz się czym są platformy Low-Code, kiedy możesz ich potrzebować oraz jaka przyszłość je czeka na przykładzie platform Microsoft jak PowerApps oraz Microsoft Flow.

Gruntowna transformacja – Zrozumieć “Dlaczego”

Zanim poznamy czym są platformy Low-Code najpierw musimy zrozumieć powody dla których w ogóle zaczęto pracować nad ich rozwojem. W tym celu cofnijmy się do lat 90tych. Wtedy to właściwie era informatyzacji rozkręciła się na dobre. E-commerce, e-government, e-society, e-wszystko zdominowało świat tak osobisty jak i biznesowy. Stało się oczywiste, że nie ma innego wyjścia jak w swojej strategii nadać wysoki priorytet informatyzacji pracy i działań. Zarządzanie danymi (Data Management), informacją (Information Management) czy wiedzą (Knowledge Management) podlegały stopniowej digitalizacji, a wraz z nimi ulegały przemianom procesy biznesowe. Wszystko po to by każdy mógł korzystać z zasobów prywatnych, zasobów firmowych, a także komunikować się z innymi z dowolnego miejsca na świecie i o każdej porze dnia i nocy.

Źrodło

Skąd wziąć nowych programistów?

Taka przemiana naturalnie wymagała zaangażowania ogromnej rzeszy specjalistów IT i inżynierów. Specjalizacje IT stały się niezwykle popularne i dobrze wynagradzane. Wszyscy robili co mogli aby zwiększyć liczbę programistów:

  • Kształocono nowych – kierunki informatyczne studiów wyższych zyskiwały na popularności. Obecnie w Polsce, jak podaje raport Ministerstwa Nauki i Szkolnictwa Wyższego, wśród wszystkich zgłoszeń rekrutacyjnych na rok 2017/2018 14% dotyczyło kierunków informatycznych, drugim było zarządzanie z wynikiem 7% wszystkich zgłoszeń!
  • Przyciągano pracowników innych specjalizacji – całoroczne szkoły programowania, bootcampy i kursy informatyczne robią wszystko by jeszcze bardziej skrócić czas kształcenia “kompletnego juniora”. Pomagają w tym także sami pracodawcy np Aviva, firma ubezpieczeniowa w 2018 roku zorganizowała 6 miesięczny kurs
    skierowany do wszystkich pracowników, dający szanse zdobycia kwalifikacji programisty
  • Zatrzymywano doświadczonych – powstałe jak grzyby po deszczu firmy rekruterskie zalewają ofertami stanowisk każdego kto na linkedIn ma cokolwiek wspólnego z IT, oferując poza bardzo dobrymi zarobkami także inne benefity.
  • Zwiększano świadomość 😉

Pomimo upływu prawie 30stu lat, sytuacja nie uległa zmianie. Szacuje się, że w 2020 na rynku europejskim będzie brakować 500.000-600.000 programistów (source, source)! W samej tylko Polsce, mojej ojczyźnie, już teraz brakuje ok. 50.000 programistów. Zapotrzebowanie na całym świecie jest ogromne i szacuje się, że utrzyma się do 2030 roku (source). Ale to nic dziwnego jeśli rozwiązania IT angażowane są w coraz to wiecej branz (HR, marketing, sztuka) i obszarów działań (prognozowanie, chatboty).

Image result for meme when a developer uploads cv

Platformy low-code na ratunek

W przypadku kiedy popyt jest większy niż podaż, są 2 strategie mające na celu zachowanie równowagi. Zwiększenie podaży, czyli większa “produkcja” developerów, zostało opisane powyżej. Natomiast sposób nr 2. polega na zmniejszeniu popytu, czyli zmniejszenie zapotrzebowania na programistów. Czy to możliwe? Okazuje się, że tak i wcale nie trzeba wysadzać w powietrze połowy firm ;). Wystarczy zbudować paltformę do skutecznego, szybkiego i elastycznego budowania aplikacji i procesów. Sama platforma musi spełniać 5 założeń:

  1. Łatwość obsługi – każdy, nie tylko wysoko wykwalifikowani inżynierowie, powinien być w stanie budować rozwiązania
  2. Wsparcie standardów – oparcie działania platformy na globalnie przyjętych i wspieranych standardach
  3. Integracja – platforma powinna umożliwiać integrację z dowolnymi systemami. Osiągnięcie punktu 2 bardzo w tym pomaga 😉
  4. Dostępność – budowanie rozwiązań jak i korzystanie z nich powinno być niezależne od wykorzystywanego systemu (Windows, Linux, macOS)
  5. Rozszerzalność – platforma której nie da się w prosty sposób rozszerzać ma nikłe szanse przyjęcia się na dłużej na rynku

I tym właśnie są tzw. Low-code development platform [LCDP] czyli wizualne podejście do tworzenia aplikacji. Microsoft posiada przynajmniej 3 takie platformy. W dalszej części artykułu skupię się 2-óch z nich: PowerApps oraz Microsoft Flow. Koegzystują one w bardzo bliskiej relacji nie tylko między sobą, ale także z ponad 230toma innymi platformami (Microsoftu jak i nie-Microsoftu).

Przykłady platform Low Code w wydaniu Microsoft. Microsoft Flow i Azure Logic Apps są bardzo podobne choć różnią się przeznaczeniem: Microsoft Flow służy głównie do procesów biznesowych natomaist Azure Lopic Apps odpowiada za integrację danych pomiędzy różnymi systemami. Zajmę się nimi w innym artykule.

W LCDP budujemy rozwiązania wykorzystując mechanizm drag-and-drop lub point-and-click z poziomu interfejsu graficznego. Dzięki temu samo budowanie, modyfikowanie i utrzymywanie jest niezwykle proste i zaoszczędza mnóstwo czasu, a efekty są widoczne natychmiast.

Przykład Microsoft Flow
Przykład Microsoft PowerApps

Korzystanie z LCDP przypomina trochę budowanie z klocków LEGO. W zabawie z LEGO używasz wyprodukowanych klocków, które są ograniczone rozmiarem, kolorem, liczbą pinów. Jednak te zależności wcale nie ograniczają w budowaniu skomplikowanych i funkcjonalnych rozwiązań.

Z wykorzystaniem LCDP można zbudować profesjonalne rozwiązania wspierające firmę i co ważne sam proces budowy jest szybki i zwinny oszczędzając czasu i pieniędzy.

Zobacz aplikację do skanowania wizytówek, której stworzenie zajęło mi 8 godzin zamiast 5 dni!

Takie podejście musi dawać efekty:

1. 70% less application development cost and effort
2. 362% return on investment over 3-year term
3. <3 months payback

Dane z “The Total Economic Impact of PowerApps and Microsoft Flow” opublikowanych przez Forrester Consulting w Czerwcu 2018 (source)

Low-Code, High-Possibilities

Za możliwości samej platformy decyduje nie tylko wielorakość dostępnych “klocków” z których możemy budować rozwiązanie, ale też otwartość platformy na integracje. Platformy PowerApps i Microsoft Flow współdzielą zestaw dostępnych connectorów których jest ponad 230!

Jednym z takich connectorów są te związane z obsługą zapytań HTTP co właściwie jest równoznaczne z możliwością integracji platformy z każdym rozwiązaniem SaaS posiadającym REST API (czyli przeważająca większość).

Po lewej: Http trigger który uruchamia Flow kiedy otrzyma zapytanie HTTP z dowolnego źródła
Po prawej: HTTP action która wykonuje zapytanie HTTP na dowolny adres URL
(Jeśli jesteś developerem, to wiedz, że stworzenie własnej usługi SaaS serveless nie mogło być prostsze!)

Ale to nie wszystko. Microsoft Flow, podobnie jak PowerApps czy LogicApps, umożliwiają zbudowanie WŁASNEGO “klocka” (tzw. custom connector). Taki Custom Connector może być połączony np: z Azure Function, które może zrealizować DOWOLNE operacje z wykorzystaniem kodu C#, Javascript, Python itp. Dobrym przykładem jest aplikacja do tłumaczenia dokumentów, która polega na tym, że:

  • Microsoft Flow wysyła do Azure Function dokument
  • Dokument jest dekomponowany przez Azure Function do pojedynczych paragrafów
  • Każdy paragraf jest niezależnie tłumaczony
  • Przetłumaczone paragrafy są sklejane w całość…
  • …i wysyłane z powrotem do Microsoft Flow

Dzięki takiemu podejściu dokument jest tłumaczony w całości z zachowaniem formatowania i układu informacji na stronie.

Wykorzystuj zasoby adekwatnie do potrzeb

Zaprezentowany powyżej scenariusz ma jeszcze jedną niezwykłą zaletę. Takie podejście jest zdecydowanie korzystne dla więcej niż 1 strony procesu wytwórczego oprogramowania:

  • LCDP developer – Może zamodelować ogólny przepływ całego procesu. Ustala z poziomu jakiej platformy użytkownik wchodzi w interakcję z aplikacją (np PowerApps), gdzie są przechowywane dane (np SharePoint Online) oraz jak przebiega ogólny przepływ informacji zwiazany z tłumaczeniem dokumentu (np Microsoft Flow). Natomiast nie wie, i nie musi wiedzieć, w jaki dokładnie sposób dokument jest tłumaczony.
  • Software developer – Z pomocą regularnego kodu (np C#) tworzy niezależną usługę, która na wejściu dostaje dokument w jakimś języku i tłumaczy go na dowolny inny zwracając przetłumaczony dokument na wyjściu. W tym celu może wykorzystać np Azure Function oraz Azure Cognitive Services. Nie wie natomiast, i nie musi wiedzieć, co się dzieje z dokumentem przed jak i po procesie tłumaczenia.
  • Business owner – wykorzystuje swoje zasoby zgodnie z ich przeznaczeniem dzięki czemu powstaje mniej błędów i dostarczona zostaje większa wartość w krótszym czasie. Osoby, które dobrze rozumieją biznes i mają szeroką wiedzę produktową mogą skupić się na modelowaniu ogólnych ram rozwiązania. Z kolei programiści mogą skupić się na tym fragmencie rozwiązania który najlepiej wykorzystuje ich specjalizację.

To bardzo ważne: korzystając z LCDP wcale nie rezygnujesz z możliwości zastosowania rozwiązań z użyciem regularnego kodu.

LCDP a Wizja jutra

Ostatnią kwestią jest: czy warto wpuszczać platformy LCDP do swojej organizacji? Pojawia się wiele pytań:

  1. Koncepcja LCDP nie jest nowa, a znana była już w latach 90tych. Czemu więc dopiero teraz zyskały na popularności? Może to tylko tymczasowa moda?
  2. Czy platformy LCDP nie zostaną niebawem zastąpione innymi platformami?
  3. Czy decydując się na jednego dostawcę nie wchodzimy w drogę jednokierunkową?

Pozwól więc, że po krótce odpowiem na te pytania.

  1. To prawda, LCDP pojawiły się wcześniej, ale ich niską popularność osobiście wiążę z brakiem odpowiedniej adaptacji standardów programistycznych (REST/GRAPH API, JSON, OAUTH). Taki brak adaptacji uniemożliwiał zbudowanie szerokiem gamy zintegrowanych platform. Natomiast obecnie środowisko IT wyklarowało takie uniwersalne standardy, a nowo budowane rozwiazania podążają za wyznaczonymi dobrymi praktykami. Jest jeszcze jeden istotny powód, a dokładniej 3 powody:
    • Wzrost – szacuje się, że wartość rynku low-code zwiększy się o ponad 21 mln w ciągu następnych 5ciu lat
    • Dywersyfikacja – zapotrzebowanie na rozwiazania IT rośnie, a jednocześnie software developerzy potrzebują odciążenia
    • Integracja – rozwiązania muszą się szybko rozwijać zachowując integrację aby móc wykorzystać potencjał stojący za dziedzinami AI, robotyki i uczenia maszynowego
  2. W całej historii rozwiązań IT rzeczywiście mnóstwo produktów pojawiało się żeby zaraz zostać sprzedanym, zmienionym lub zabitym. Dlatego osobiście wydaje mi się, że najlepiej wiązać się z rozwiązaniami dużych firm, których przyszłość opiera się na zaufaniu swoich klientów. To zaufanie zależy bezpośrednio od posiadanych rozwiązań, a także ich stabilności, przewidywalności, rzetelności oraz bezpieczeństwa powierzanych danych. “Zaufanie czyli Waluta Przyszłości” jak głosi tytuł znakomitej polskiej książki autorstwa Michała Szafrańskiego.
  3. I tak i nie. Z jednek strony rzeczywiście wybierając KTÓREGOKOLWIEK z dostawców procedura wyjścia może być wiązać się nawet z koniecznością przepisania rozwiązania na nowo w przypadku chęci zmiany. Z drugiej jednak strony budowa interfejsu graficznego nie powinna zająć dużo czasu, a w przypadków systemów RPA można zaimplementować krytyczne fragmenty rozwiązań jako niezależne rozwiązania SaaS (np z wykorzystaniem Azure Functions czy web aplikacji)

Podsumowując: dla mnie osobiście Low-Code development platforms są koniecznością każdej firmy chcącej dynamicznie się rozwijać. Niosą ogromną rewolucję jaką swego czasu przyniósł interfejs okienkowy w systemach operacyjnych. Z jednej strony, to tylko zmiana interakcji z procesorem “po drugiej stronie”. Ale z drugiej, to otwarcie na zupełnie nowe możliwości.

Gdybyś chciał dowiedzieć się więcej o tych platformach napisz do mnie! Nauczę Cię z nich korzystać, a jeśli zechcesz pomogę zaprojektować rozwiązanie albo po prostu zbuduję je dla Ciebie!

Overview of Microsoft Build 2019

In this article I’ll put short glimpse of what has been announced on Microsoft Build 2019 conference.

flow PowerApps

  • Develop custom components for PowerApps

    This is going to be a big thing for PowerApps. From now on PowerApps apps can have embedded custom controls that have been built using code. Currently only model-driven apps are supported but canvas apps are on the roadmap!

  • Solutions 

    Ok, this is nothing new int terms of platform capabilities but it’s great to know Microsoft is continuously investing in CDS Solutions user experience.

  • PowerApps checker

    When it comes to the CDS Solutions it’s worth to mention that a great tooling comes along with it – a PowerApps checker which that analyse a CDS Solution and identifies potential performence and stability risks across all the assets in the solution.

flow Microsoft Flow

  • Import Azure Function / Logic Apps as Custom Connector 

    Until now this was possible but requires some work on Azure platform and then on Flow studio. Because of such context inconsistency  in the result for some of the users and devs the whole process of publishing azure function as a custom connector and using it in a Flow or PowerApps wasn’t clear enough. And this has been improved!

  • Improved Application Lifecycle Management for flows using CDS 

    Flows that are part of the CDS Solutions can now be automatically activated on solution import to a new environment!

  • Integrate your Azure Blockchain Service with anything using Microsoft Flow 

flow Microsoft Teams

  • Message actions

    One of my favorite things in Microsoft Teams is how it’s greatly integrated with other SaaS. I was already very satisfied but Microsoft Teams does not stop in surprising me – this time with context actions so from withing a conversation you can click on a message and create a task based on it!

  • Link unfurling

    Whenever someone paste a link to a chat message it will be previewed as an image. Simple and useful. Keep in mind that you can also integrate it with your app.

  • Low code teams app templates

    If you read my blog you should already know I’m a big fun of Low Code solutions. I think they will fill a great gap between end-users and IT world with developers on the lead. My favorites LCDP from Microsoft are of course PowerApps and Flow but Microsoft Teams is few steps behind them

flow Fluid Framework

A new feature that is going to revolutionize the way how people collaboratively work on the same document with each other. Long story short: Digital workplace besides being an advocate of working wherever you are it also states a very important rule – consistent context of all tools you use in your office. In Microsoft such tool that allows you to integrate with anything but keeping the same context is Microsoft Teams. Fluid Framework bring this idea to a higher level so i.e. you could work on the same document directly from a chat seeing only a part of the document that is the most related to your conversation.

flow Microsoft Search

Bing + AI + Microsoft Graph = Microsoft Search. The new search from Microsoft not only takes the best from the above but also is everywhere! It’s integrated with Office, Outlook, SharePoint, OneDrive, Bing, Windows etc. One Search to Find Them All. It will serve you best then ever.

Business Card Reader

On my session during last SPS Warsaw I showed 5 examples how I save my time by automating things that steal my time almost everyday. In this blog post I’ll show you one of those solution. And that’s not all. I’ll show you how it’s been built so you can build it for your own…or just install my package and save your time!

But firstly sketch shortly the context – is this solution really helpful for me (or you)?

Business card business

If at some point of your career you’ve worked close to business development or you know what networking is all about then probably you already know that business cards are still very popular. Yes, there are apps that support NFC business info exchange but somehow classic paper business cards are still number 1.

Image result for meme business cards

And I agree: business cards are awesome! They look fancy, are easy to share, their size fit into pocket or wallet and if you hit a very unique business card (i.e. made of rare material or fabric) it’s always a good topic to chat during coffee break. However in the end of the day you always end up with a whole pile of those. And the worst part in it is…your workplace! It’s digital. And paper is not compatible with computers (sic!) unless being digitized.

So once after such networking events when I ended up with almost 10 business cards I said to myself that I have to do something with it. I opened one of my favorites productive apps and scanned the first business card from the pile: Capture > Import to > ….only Onenote? Eeee. I opened App Store and briefly look for some business card scanner that could import a business card to outlook. 5 minutes later I decided that actually I have everything I need to relatively quickly and flexibly develop my own business card scanner!

PowerApps and Flow give me flexibility, efficiency and reliability I need. No code solutions with almost a full potential of code solutions. Legit.

Business Card Reader architecture

The architecture of the solution is this:

  1. PowerApps takes photo and passes stream to Flow
  2. Flow convert photo stream (as URI) to binary and passes it to Cognitive Service Vision OCR
  3. Cognitive Service Vision OCR is a beautiful blackbox which accepts image content and returns JSON as described here (note that each text is linked to a bounding box coordinates so theoretically you could use this information to highlight text in a picture. Check Paul O’Flaherty great video for reference)
  4. Flow returns JSON with all recognized strings to PowerApps
  5. PowerApps parses results (i.e. joins number values) and waits for a user to match all fields. Once a user done it PowerApps pass fields values to Flow…
  6. …so it could create new contact in my Outlook contact group

The whole solution works like this

Describing the whole code of the solution wouldn’t make sense (it will be so long that I doubt anyone will read it) but let me put emphasis on some of the most neat constructs in my solution.

PowerApps Business Card OCR Flow Response

Until now I used to use standard PowerApps response from Flow actions. (i.e. I used and described it in this solution which you can also download and play with). But this time I didn’t do that and used HTTP Response action instead.

Thanks to this move I didn’t have to parse JSON results in PowerApps (as I did when I was integrating SharePoint Search results in PowerApps) because PowerApps already was able to interpret such JSON object as Record type. In other words I could save JSON results as record variable (or even a collection if I would) and use it as any other variable. Everything done smoothly, using one, sexy line of code (yes, code can be sexy. The same as math equations).

Set(CapturedRegions,PowerAppsBusinessCardOCR.Run(Image4.Image).regions);

Matching recognized strings with fields

This step has been done with an intention to be universal so I could use it not only with business cards but any other OCR use case where matching could/should not been done automatically i.e.: invoice scanning, diploma scanning, sign scanning, random paper text scanning etc.

Cognitive Service Vision OCR number parsing

Normally Cognitive Service Vision OCR recognizes number sequences with
white spaced as seperate strings. So what I did is wrote a simple logic which separate numbers from text values, joins numbers together and pushes back to recognized text collection. Code looks like this:

Avoiding code duplication

In matching screen there is a logic which control which variable will be updated once user select an item from recognized text collection gallery. Because this code could be used in multiple places (or at least that was the idea at the beginning) I didn’t want to maintain more than once. So I used a trick which I describe in this post. Long story short the idea looks like this:

  • Add a new button and make it invisible
  • Put your redundant formula in OnSelect event of the button
  • Use Select(YourInvisibleButton) function to execute formula inside OnSelect event of your invisible button
  • For any dependencies (you can think of them as the functions arguments) use variables (global or local).

Styled confirmation screen

Styled confirmation screen makes that every time I confirm new contact I’m smiling. Seriously. No joke. It feels like all code logic in designed to be transparent for a user. No hidden catches, back-end formulas and processing that results in user’s “WTF?!” when he confront app results with his expectations. As a developer I know that there is nothing happening to business card contact fields values and confirmation screen is overhead. But as a user it’s just nice to be able to make sure what will be imported to my outlook contacts. Do you agree with me?

Download my solution

Feel free to download my solution. When you’ll install it you should have 1 PowerApp, 2 Flows and 2 additional connectors.

Remember to configure connectors and be aware that for Cognitive Service you’ll need Azure subscription with set up Cognitive Service resource. To sign up for cognitive service read this documentation section.

And that’s it! I hope you enjoy this post. Let me know if you like it or have any questions!

Excel row does not exist in Flow

So here I am working on another PowerApps app. My app should work across different tenants so I’m using Flow HTTP request/response for data sync purposes. For a storage I’m using an excel file on my OneDrive for Business.

One of my Flow use cases to cover was to get JSON data from HTTP request and:

  • if row exists – update it
  • if row doesn’t exist – create a new one

I’ve quickly searched the web looking for most recommended approach and found this article where a promoted solution suggest to:

  • Get all rows from an excel file
  • Filter all rows looking for a specific one
  • Using condition action split a flow to “have been found” and “haven’t been found” paths

And this is actually a quite good approach…unless there is nothing much happening after a conditional split. For better understanding consider following:

  • Get all rows from an excel file
  • Filter all rows looking for a specific one
  • Conditional split:
    • Have been found -> do action A -> finish
    • Have been found -> do action B -> finish

Better approach (in specific cases)

Above bullet point list describe a pretty simple scenario but represents exactly my need. And because of its simplicity I was wondering “do I really need to get ALL rows only to find out if there is the one I’m looking for?”. The answer was obvious: “no, I don’t”.

The trick is to use error handling capability of Microsoft Flow along with Termination action with “Succeeded” status.

Result when my HTTP trigger received a row guid that does not exist:

I hope you find this short article helpful in your Flow implementations.

How to overcome 500 items limit in PowerApps

I planned to publish another topic for today but I decided that this one will fits better to the post I’ve published a week ago.

So if you work with PowerApps connected to ANY data source you had to experienced or at least read about 500 items limitation. In this post I will show you 6 ways how you can exceed that limitation.

Limit? What limit?

If you’re one of those happy devs that have never heard of such limit I’ll make quick reminder:

In PowerApps every data source (SharePoint, Common Data Service, OneDrive) is under limitation of 500 items. It means you cannot get more that 500 items from a data source but even more than that – PowerApps won’t even “be aware” of any rows above 500.

Imagine simple case: On your OneDrive for Business you have an excel file with ~600 rows. Even though you need only last 10 rows you will get rows 490-500 instead of 590-600. As I told you in previous paragraph – it’s not the limit of total fetched items. It’s the limit of items that PowerApps knows about.

But before you think that’s a killer for PowerApps and “oh those architects, they’ve never use their tools so how they can design something that actually works” let’s imagine another example:

You have an excel with 2mln rows (yep, I saw over 2GB excel file in one company from financial sector). One of app users opens your app and…probably in an eyeblink you will get tons of emails that your app is not working (but of course it is working but the loading time is huge). That’s why this limitation make sense.

Anyway there are many occasions where you will need more than 500 items. Maybe not 2mln but a simple task list for a company with ~1000 employees can easily generate around 50000 rows in total. So how you can overcome this 500 items limitation in PowerApps? In following sections I describe 6 options in the order from easiest/fastest to the most powerful (but also time consuming to setup):

  1. Increase the total limit items you can fetch
  2. Use static data
  3. OneDrive for Business connector specifics
  4. Use delegation
  5. Use delegation + iterative function
  6. Combine PowerApps with Flow

1. Increase the total limit items you can fetch

Okey so this method is the easiest and really quick to setup. Click file (top left corner) > App settings > Advanced settings > set value for non-delegable queries.

This method has one hard limit (limitation of the limitation) – 2000 is a maximum value you can set which means you can’t get more that 2000 items on the same rules as for 500 items (PowerApps won’t know about 2001 item). So if this method does not work for you let’s move to option 2.

2. Use static data

In some specific cases static data may be the best solution. Such data can be imported to your app and will be kept within assets of your app. You can have 10000 rows and still users will have access to all rows. However remember following notes:

  1. Static data are static – you cannot modify it from within PowerApp. But for some scenarios it may still do the job i.e. you build company travel app and you want to have index of all countries in the world (192) along with bigger cities (~30 x 192=5760) so your users can search and select to which city they’re traveling to. Countries and cities typically don’t change dynamically so we can freely import them as static data instead of using SQL database (for which you have to spend extra cash).
  2. Static data are attached to your app which means they’re enlarging total size of app which may affect app loading time.

If you still think static data is a best choice for you here is how you can add it:

  1. View > Data Sources > Add data source
  2. Click Import from Excel
  3. Select excel file and a its table that you want to import

Ok, now, what if this option also doesn’t suit to your needs: Excel is good but you need to be able to modify it from within app so all app users have access to same data. So you googled and decided to use OneDrive for Business data source connector. It uses excel file as a container which data can be modified, sounds perfect, right? So here is the deal…

3. OneDrive for Business connector specifics

Accessing Excel files using OneDrive for Business connector does not support delegations (I’m explaining delegations in section 4). Long story short here are the implications:

  • You can’t get more than 2000 rows
  • 2000 is the total number of rows despite of the number of tables or worksheets in excel file
  • If you have more than 2 tables, O4B connector firstly access all rows from Table1, then from Table2, then from Table3 and so on until reach the number of items equivalent to value for non-delegable queries.

So if you know you may need more than 2000 rows stored in one data source you should switch from OneDrive for Business to any other like SharePoint, Common Data Service, SQL etc. and take use from delegable queries. What are these? Let’s see.

4. Use delegation

Delegation is a mechanism to access all data from a data source in a performance friendly manner.

Speaking a bit more clearly it’s a situation when your PowerApp app says to a data source:
“Ok, you know what, I need items that match these conditions but hey, can you do all the computations by yourself? I need to use my network bandwidth, memory and CPU power for something else…I just need results.”

Now, there are 3 catches:

  1. All results are fetched in a maximum of 100 items bundles. The next bundle is being fetched once a user scroll to the end of a gallery/table list (check out delegation demo gif down below)…
  2. …which means if you need to fetch more than 100 items (! not process but fetch. You can process 10000 of items but fetch only 15 of them as result) you can only use gallery or table. Collections are not supported and works under non-delegable queries limitation (Collect() or ClearCollect() functions breaks delegation!)
  3. Your query need to be supported by the data source. Figuratively speaking the data source must understand what PowerApps app is saying to it (check out example queries on the image down below). To complicate it a bit more not every data source supports all functions and operations – each data source connector documentation outlines delegable support (i.e. here you can find SharePoint Online delegation support). For better understanding of delegations in PowerApps I recommend you to read delegation documentation.
Delegation demo. Left gallery is sourced by OneDrive for Business (which is non-delegable). Right gallery is sourced by SPO list with 500+ items.
Look what happens once I scrolled to the bottom of the SPO items gallery.
Example delegable and non-delegable queries in PowerApps

Ok but what if need more than 2000 items here and now? There are 2 options for doing that. But before you read them please consider below:

Following approaches should be treated as potentially bad practices and should be used only under specific circumstances and with proper caution since they may have bad impact on your application performance or other O365 tenant services. It’s like with medicines – they can solve your problems but harm you in wrong dosage or used inadequate to needs. So in 95% of cases delegation will perfectly do the job. It may requires read & learn a bit but trust me – it’s easier than struggling with performance issues that may appeared if you misuse options 5 and 6.

Ok, so now we can safely move to options for those 5% of cases 🙂

5. Use delegation + iterative function

General idea is like this: for a delegable query build a loop and in each iteration filter data chunks. I.e. in 1st iteration you get rows 1-500, in 2nd 501-1000 and so on.

I won’t explain it in detail or show you code snippets because this method is not mine. Its author is MS employee Brian a.k.a Mr.Dang() and I would feel bad if I get his credits for this workaround. Read it and just in case it won’t meet your needs…

There is also another way of which I’m the author. Option 6. Here it comes.

6. Combine PowerApps with Flow

The last option is to use Flow as a middle man that was asked this:
“Dear Flow, since your data source connectors don’t care about this whole delegation stuff, can you please do me a favor, get all rows, join them in 1 big string and send me back please?”

A simple demo with excel and outlook email may look like this:

To make this work for more than 256 results I had to make few configuration tweaks of the “List rows present in a table” action:

Fetching 682 rows took 3 seconds.

Fetching 2101 rows took 11 seconds so that may be an issue.

However if you use SharePoint as a data source I have a good news for you – I’ve made a demo that fetches 1000 items in 2 seconds. In this blog post I describe step by step how to:

  • Build a PowerApp from scratch
    • Add input fields
    • Pass input fields values to Flow
    • Parse results from flow
    • Display all results in a gallery
  • Build a Flow from scratch
    • Get parameters from PowerApps
    • Integrate Flow with SharePoint
    • Send all results back to PowerApps

And that’s it! I hope you enjoy this blog post. If so please let me know in the comments down below. If not or maybe you know a better solution – let me know as well!

Performance – is Flow faster than PowerApps?

In this post I’ve showed you how you can create PowerApps app that utilize SharePoint Search for your business. I used there PowerApps for parsing big string containing our results from SharePoint Search.

But one o my readers asked me an interesting question

“Mike, wouldn’t it be easier and faster if we parse results in Flow and pass to PowerApps just raw field values?”

Jacek M.

Hm….at that time I didn’t know the correct answer. Neither I could find it in the PowerApps Canvas App Coding Standards and Guidelines . That’s why I’ve decided to make quick test on my own and publish its results on my blog.

Time measurement 1: parsing on PowerApps app side

It’s pretty straight forward to measure parsing time for the application we’ve built in my previous post. Just add timer control, start the timer before firing flow, fetch results from flow, parse it and stop the timer right after it. It will look like this:


I’ve setup my test to fetch 10 results. Result of it: 1 second! 1 second is the time that PowerApps need to fetch 10 results from flow and extracting required field values (Title in this case). But that was not enough to me. I thought: “let’s see how long will it takes to get 100 results and extract 4 fields”. The results surprised me. 1 second again!

3 of 100 fetched items from SharePoint Search via Flow. PowerApps extracted 4 fields from received string: Title, URL, Ranking Sum and UID of the item.

It will be very hard to beat it but let’s give it a shot! Let’s check how Microsoft Flow will manage to extracting field values.

Time measurement 2: parsing on Flow side

First let’s start from small test that is fetching 10 items and only Title field. The modification of the flow is simple: I will iterate through each row of the SP search results, then through each cell in a row (each row contains multiple cells) and then once I find the cell I’m looking for (Title in this case) I’ll append cell value to an array variable. Modified flow will look like this:

Parse Cell action allows for using Key, Value, ValueType attributes in farther actions. The Join action that is in the bottom of the screenshot is used to convert array to text value (because PowerApps does not accepts arrays 🙁 ).

For start I will just take 10 results and extract only Title field. Let’s test this configuration:

WHAT?! 32 seconds!? I must admit – I expected that Flow will be slower that PowerApps but not ~30 times (it’s obvious why it’s a common pattern to use your end user CPU time, via browser or mobile memory, rather than your own) . That’s huge difference and strong argument for using PowerApps for all kind of parsing and any CPU expensive calculations and not using Flow for that.

Further testing (more fields, more items) has no point. Let’s jump right to the conclutions.

Conclusions

Personally I find this small test really informative.

First: It’s more efficient to use PowerApps for calculations that Flow. It’s worth to tell it even if most of us know that common good practice is to distribute calculations and delegate it to user device.

Second: In the light of above note, consider making more than 1 call to Flow and back to make most of the expensive calculation on the PowerApps side for the sake of whole process time consumption. E.g.:

  1. PowerApps asks Flow to get results from SPO Search
  2. Flow get results and pass it back to the app
  3. PowerApps extract some data (specific fields, values range, values sum etc) and pass it back to Flow
  4. Flow gets additional data for selected items or fields and pass it back to PowerApps again
  5. PowerApps display results to user
Check how I can help you or contact me.

What do you think about that? Does this test was also informative for you? Or maybe I missed something that may change whole results – let me know!

Search in SharePoint from PowerApps

Inspired by this post I realized that built-in connectors does not allow to utilize SharePoint Search. So theoretically it is not possible to use SharePoint search from PowerApps. However using Flow as a middle layer you can call SharePoint Search and parse results for your needs. Let’s see how to do that.

Why you may find SharePoint Search useful in your app?

Let’s consider is it worth to have such possibility as using SharePoint Search from your PowerApps app. To answer this question let’s see what we can and cannot do using built-in connectors.

You can:

  • Get content of any list or library in a single SharePoint site
  • Read, Update, Delete elements of list or library
  • Use specific subsets of items (filtering, sorting) in delegate manner
  • Get content of Office 365 Groups
  • Get OneDrive content (only excel files)

You cannot:

  • Find all items of a specific Content Type across whole tenant
  • Get elements from list or library in a site – even if the list or library was created after the connector has been declared
  • List SP sites in your tenant
  • Get OneDrive for Business content (all kind of)

All above are limitation coming from OOB SharePoint connectors. So if you want workaround them this post will tell you how to achieve it.

PowerApp SharePoint Search Architecture

We need 3 components:

  • PowerApps: acts as front-end, provides screen and controls to user and also display results,
  • Flow: acts as back-end, call SharePoint search (also keeps SPO url) and pass results to PowerApps
  • SharePoint: contains all the data of course

Those components will communicate each other in following manner:

  1. PowerApps uses Flow connector to make a call to it
  2. Flow uses “Send an HTTP request to SharePoint” and then…
  3. …parse returned result to JSON
  4. Flow join all rows as string variable and pass it as to PowerApps as respond
  5. PowerApps filters out the data it needs

Let’s start building it! To not make this post too much long I’ll focus only on most important part skipping parts like header creation or pagination functionality.

Follow this link to download complete demo app (remember that url to SPO is in the flow)

Step 1: Create a PowerApp

First we need to create an app, add screen to it and add controls to the screen (if you don’t know how to do any of those please check out this post). Some parts that may need additional explanation:

  • numRowLimitNumber – number of items to return
  • btnCallFlow – this is invisible button that call Flow on OnSelect event. Other controls (like previous/next page buttons) can fire it using Select() function instead of duplicating code in it.
  • galSPSearchResultItems – gallery displaying results items
    • imgOpenInNewWindow – clicking it will open SharePoint item in browser
    • lblSPItemTitle – displays item Title
    • lblSPItemUniqueId – displays item UniqueId
    • lblSPItemRank – displays item ranking sum. Because why not 😉

Now let’s configure behavior of our controls.

  • On OnSelect action of the “Search” button put as follow:
Set(gblItemsToSkip,0);
Set(gblPaginationVisibility,false);
Select(btnCallFlow)
  • As you can see it fires btnCallFlow. On OnSelect action of that button we will call the flow
Clear(colSPSearchResultItems);
Set(
    gblRowLimitNumber,
    numRowLimitNumber.Text
);
Set(
    gblSearchResults,
    Searchforitemsintenant.Run(
        txtSearchPhrase.Text,
        numRowLimitNumber.Text,
        Text(gblItemsToSkip)
    )
);
<HERE WE WILL PUT CODE TO PARSE SP SEARCH RESULTS>
Set(
    gblPaginationVisibility,
    true
)

Now let’s add flow to our app. To do that click:
Action tab -> Flows button -> “Create a new flow”

Step 2: Create a Flow

On Microsoft Flow side add variables that will keep input parameters from PowerApps. Then add “Send and HTTP request to SharePoint”.

The Uri attribute contains function that replace apostrophe (‘) to empty sign in case user put ‘ in the search phrase (accidentally or intentionally) which will break the query:

_api/search/query?querytext='@{replace(variables('SearchQuery'),'''','')}'&clienttype='ContentSearchRegular'&selectproperties='UniqueId, Title, OriginalPath'&rowlimit=@{variables('RowLimit')}&startrow=@{variables('RowsToSkip')}

Next add Parse JSON with Body output in Content field and a Schema.

The schema however is a bit tricky because using sample (i.e. from search REST call using browser or Postman) may not include all variations of results (that was my case). So after quick debugging I’ve successfully created following schema – feel free to copy-paste it.

{
    "type": "object",
    "properties": {
        "odata.metadata": {
            "type": "string"
        },
        "ElapsedTime": {
            "type": "integer"
        },
        "PrimaryQueryResult": {
            "type": "object",
            "properties": {
                "CustomResults": {
                    "type": "array"
                },
                "QueryId": {
                    "type": "string"
                },
                "QueryRuleId": {
                    "type": "string"
                },
                "RefinementResults": {},
                "RelevantResults": {
                    "type": "object",
                    "properties": {
                        "GroupTemplateId": {},
                        "ItemTemplateId": {},
                        "Properties": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "Key": {
                                        "type": "string"
                                    },
                                    "Value": {
                                        "type": "string"
                                    },
                                    "ValueType": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "Key",
                                    "Value",
                                    "ValueType"
                                ]
                            }
                        },
                        "ResultTitle": {},
                        "ResultTitleUrl": {},
                        "RowCount": {
                            "type": "integer"
                        },
                        "Table": {
                            "type": "object",
                            "properties": {
                                "Rows": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "Cells": {
                                                "type": "array",
                                                "items": {
                                                    "type": "object",
                                                    "properties": {
                                                        "Key": {
                                                            "type": "string"
                                                        },
                                                        "Value": {
                                                            "type": [
                                                                "null",
                                                                "string"
                                                            ]
                                                        },
                                                        "ValueType": {
                                                            "type": [
                                                                "null",
                                                                "string"
                                                            ]
                                                        }
                                                    },
                                                    "required": [
                                                        "Key",
                                                        "Value",
                                                        "ValueType"
                                                    ]
                                                }
                                            }
                                        },
                                        "required": [
                                            "Cells"
                                        ]
                                    }
                                }
                            }
                        },
                        "TotalRows": {
                            "type": "integer"
                        },
                        "TotalRowsIncludingDuplicates": {
                            "type": "integer"
                        }
                    }
                },
                "SpecialTermResults": {}
            }
        },
        "Properties": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "Key": {
                        "type": "string"
                    },
                    "Value": {
                        "type": "string"
                    },
                    "ValueType": {
                        "type": "string"
                    }
                },
                "required": [
                    "Key",
                    "Value",
                    "ValueType"
                ]
            }
        },
        "SecondaryQueryResults": {
            "type": "array"
        },
        "SpellingSuggestion": {
            "type": "string"
        },
        "TriggeredRules": {
            "type": "array"
        }
    }
}

At the end, we just have to join rows (using some special separator like “/n” which is unique enough) and pass it back to PowerApps along with totalRows number

If you did everything properly your Flow should look like this

Now we can get back to PowerApps and parse the results.

Step 3: Parsing results in the PowerApps

Flow pass result to PowerApps in a string field (called “results”). But we cannot use it in a gallery right away. Just look at it:

{"Cells":[{"Key":"Rank","Value":"17.1283779144287","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17594717461461","ValueType":"Edm.Int64"},{"Key":"Title","Value":"Michał Guzowski Team Site","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595142086123","ValueType":"Edm.Int64"},{"Key":"Title","Value":"pl","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595383574480","ValueType":"Edm.Int64"},{"Key":"Title","Value":"en","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595390074495","ValueType":"Edm.Int64"},{"Key":"Title","Value":"fr","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595412154536","ValueType":"Edm.Int64"},{"Key":"Title","Value":"da","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595412284536","ValueType":"Edm.Int64"},{"Key":"Title","Value":"de","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595451564436","ValueType":"Edm.Int64"},{"Key":"Title","Value":"sv","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595498094501","ValueType":"Edm.Int64"},{"Key":"Title","Value":"lv","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0755615234375","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17595513184435","ValueType":"Edm.Int64"},{"Key":"Title","Value":"ro","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}"/n"{"Cells":[{"Key":"Rank","Value":"17.0754928588867","ValueType":"Edm.Double"},{"Key":"DocId","Value":"17594717461464","ValueType":"Edm.Int64"},{"Key":"Title","Value":"How To Use This Library","ValueType":"Edm.String"},{"Key":"PartitionId","Value":"2037f0e8-05e7-4df7-bbe9-b8cf7c8af72a","ValueType":"Edm.Guid"},{"Key":"UrlZone","Value":"0","ValueType":"Edm.Int32"},{"Key":"Culture","Value":"en-US","ValueType":"Edm.String"},{"Key":"ResultTypeId","Value":"0","ValueType":"Edm.Int32"},{"Key":"RenderTemplateId","Value":"~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js","ValueType":"Edm.String"}]}

How to extract specific parts of that?

  • Split string with our special delimiter: “/n”
    • For each string part we split it once again using following scheme:
      • “_FieldName_”,”Value”:
      • I.e. for Title field it will looks like this “Title”,”Value”:
      • Also we need to encode quote mark (“) so example after encoding looks like this: Char(34)&”Title”&Char(34)&”,”&Char(34)&”Value”&Char(34)&Char(58)&Char(34)
    • We take second element (above split always returns only 2 elements) and split one more time using following scheme:
      • “,”ValueType
      • After encoding it look like this: Char(34)&”,”&Char(34)&”ValueType”
    • We take first element (above split always returns 2 elements) and add it as value of a specific column.

Wow, felt a little dizzy? Don’t worry. Just copy below code, paste it in your app and replace name “Title” with field name you want to extract.

ClearCollect(
    sposearchitems,
    AddColumns(
        Split(
            Items,
            "/n"
        ),
        "Title",        First(Split(Last(Split(Result,Char(34)&"Title"&Char(34)&","&Char(34)&"Value"&Char(34)&Char(58)&Char(34))).Result,Char(34)&","&Char(34)&"ValueType")).Result
    )
)

In my case (for fields UniqueId, Title, OriginalPath and Rank) my parse operation looks like this:

Step 4: Display results

Finally! We made up to this part. Just set the values of labels in the gallery and enjoy the results!

For me the final result looks like below but your can be totally different (and better than mine ;] )

THAT’S IT! Wasn’t that hard, was it? 😉 If it was, remember that you can download my Demo SharePoint Search PowerApps application to compare or just copy paste some parts – just click here!

Hope you find this post useful. If so please share it and comment if you like.

Thanks and have a great coding 🙂

Update 22.02.2019 – If you’re curious if you can use Flow for parse computations I’ve done such comparison in my next blog post.

26.02.2019 – O365 User Group PL Warszawa #1

Breaking news: I will be a speaker on the first O365 User Group PL Warszawa meeting. Woohoo!

I will tell about how to build smart app that enthused MVPs and Microsoft employees around the world (also known as “the story of 1 tweet” 😉). But I’m just a shadow of the others: hosts Michał Słowikowski and Kamil Bączyk and another speaker: Microsoft Teams Product Group representative: Jace Moreno !

O365 User Group PL Warszawa #1

Tuesday, Feb 26, 2019, 6:00 PM

Microsoft Polska
al. Jerozolimskie 195A Warszawa, PL

57 Członkowie Attending

Zapraszamy na inauguracyjne spotkanie grupy Office 365 w Warszawie. Jako, że mamy wyjątkową okazję to sesje i agenda również takie będą. Podczas tego spotkania poruszymy tematy związane z platformami Teams oraz PowerApps. Agenda: 18:00 – 18:15 Intro 18:15 – 19:05 Unleashing the Power of the Microsoft Teams Platform 19:05 – 19:15 Przerwa 19:15 – 2…

Check out this Meetup →

Enable Whistleblowing in your Office365 tenant with PowerApps

PowerApps can participate in organization modernization in many ways. And I’d like to show you some example. Imagine following scenarios:

  • You’ve seen how a colleague sends confidential documents to his private email. Or…
  • By accident, you heard two colleagues talking about “dorabianiu” at the expense of the health of patients by ordering unnecessary radiological examinations. Or…
  • In the network, one of your colleagues has published an offensive text that hits the brand of the company in which you work. Or…
  • You and a few colleagues from work are the object of mobbing and microaggressive behaviors

I hope you don’t know what I’m writing about but unfortunately many of the above situations really happened: example1, example2, example3.

Such incidents may put significant questions marks on your organization reputation, reliability and honesty followed by financial penalties depending on the seriousness of the offense. So to protect your organization Microsoft provides multiple different tools such as Azure RMS (Azure Rights Management), DLP (data loss prevention) and retention policies. But non of those tools gives you an easily accessible way to pass on information concerning wrongdoing in safe and anonymous manner. That inspired me to create a solution that will fill the gap.

Enable whistleblowing

72 per cent of Canadian survey respondents recognize cyber crime as a risk, many still don’t fully understand the potential impact a cyber breach can have on the business

Financial Post article

The original article of the above quote also noticed that enabling whistleblowing allow for early identification of issues and is critical for ability to manage risk. That inspired me to create a solution integrated with O365 that will respect user anonymity – at least on the data access level.

I’m a huge fun of PowerApps but unfortunately they do not allow for guest access (at least not yet! 😉) and I was wondering if that’s hard limitation. Maybe there is some workaround? There must be. And with a small help of Microsoft Flow I was able to create a solution that:

  • Allow for anonymized creation of new submission
  • View, Edit ones submissions in anonymized manner
  • Correspond with assigned admin in anonymized manner
  • As Admin you can access all submissions as well as filter submissions by status (i.e. only those submissions that waits for your action)

Power Whistleblowing app – user view

This is animated view – If gif is not animating click here

Another view (this time just a screenshot)

Power Whistleblowing app has also an admin view

This is animated view – If gif is not animating click here

Power Whistleblowing architecture

The architecture of above solution is simple:

  1. PowerApp gets information from a user and pass to flow. On this stage everything is personalized. We know who send what.
  2. Flow pass over HTTP request to another flow with parameters of newly created item (for submission it’s: Title, Description, Category; for Comment it’s: Author GUID, Submission GUID, Comment). This is the moment where we lost all context information (except data that are essential for the business logic) and imply anonimization
  3. For newly created submission we need to generate Author GUID (I’ll explain later what is its role) and pass it back to first Flow.
  4. For newly created submissions the Flow expects the Author GUID and pass it back to PowerApps app
  5. Both for new submission and new comment all information are saved in SharePoint Online impersonated as Service Account. We don’t know who is original creator of the record, we only have some Author GUID

Why do we need Author GUID?

Author GUID is the new credentials for submitter to:

  • Check status of his submissions
  • View all his submissions
  • Leave a comment in any of the submissions (to correspond with the admin)

I find this solution really useful for an organization – what do you think? Leave me a comment! Oh and also feel free to ask freely on any other topics i.e.:

  • How to create HTTP connected Flows
  • Is it possible to build admin panel with vertical tabs (yep, it’s tricky 😉)
  • How to build Regular Expression to check GUID cohesion

I don’t bite but do drink beer. You can also catch me on my fb, twitter, linkedIn or PowerUsers forum.

Just in case you want to:

  • Deploy this solution on your environment
  • Customize it for your own needs
  • Create new solution based on this one

Contact me and I will help you

…Oh, and I have a small gift for all of you that read until now – you can download this solution here. Sharing is Caring. Enjoy!