PowerApps+Flow logo

Szykując się do nowego projektu, zacząłem sprawdzać, czy różne oczekiwania klienta są możliwe do zrealizowania z użyciem PowerApps. Pierwsze z nich to możliwość użycia pliku XML jako danych wejściowych, do zbudowania tabeli w aplikacji i ew. późniejszego ich zapisania do SQL.

Nic prostszego, pomyślałem. W końcu w PowerApps istnieje kontrolka „Attachment”. A jak nie, to na pewno jakaś inna, którą można użyć.

Dowiedziałem się przy tej okazji, że kontrolka „Attachment” jest dedykowana formularzom powiązanym z listami SharePoint i niespecjalnie można ją od tak sobie umieścić w aplikacji. Ale istnieje inna, która nazywa się „Add picture” – choć nazwa sugeruje użycie jej wyłącznie w celu dodania obrazu, ma ona dużo bardziej wszechstronne zastosowania.

Jak wgrać plik do aplikacji?

A zatem kontrolka do dodawania obrazu. Znajduje się ona w grupie „Media”:

"Add picture" control in PowerApps
Kontrolka „add picture”

Choć faktycznie sugeruje, również po dodaniu, że służy wyłącznie do wgrywania zdjęć, to jednak kluczowe jest zdanie z opisu: „Let users upload image through your app”. A skoro pozwoli na wgrywanie zdjęcia, to zapewne i innych formatów plików, prawda?

Po wybraniu jej w aplikacji dodawana jest grupa, złożona z dwóch kontrolek. Jedna, to „AddMediaButton„, druga to po prostu kontrolka wyświetlająca obrazek. Mi zależało wyłącznie na tej pierwszejm dlatego też drugą usunąłem:

AddMediaButton in PowerApps
AddMediaButton

Działanie kontrolki jest następujące:

  1. Użytkownik klika na nią
  2. Pokazuje się okno wyboru pliku (na szczęście są w nim „Wszystkie pliki”)
  3. Użytkownik wybiera plik
  4. Plik jest dodawany do aplikacji, umieszczany w lokalnym blobie.

W moim scenariuszu dodatkowo umieściłem walidację, żeby możliwe było dodawanie jedynie plików XML. Dla zdarzenia „OnChange” kontrolki do wybierania plików, dodałem taki kawałek kodu:

IIf(
    EndsWith(FileUpload.FileName, ".xml"), 
    [true], 
    [false]
)

Czyli, jeśli rozszerzenie pliku nie kończy się na „xml„, wówczas pokazywany jest błąd oraz przycisk do wgrywania jest wyłączany.

Jak obsłużyć wgrany plik?

Tutaj do gry wchodzi naturalnie Microsoft Flow. Jednak, aby plik przesłać do przepływu nie wystarczy po prostu użyć „FileUpload.Media”, ponieważ dane pliku są w local blob i takie wyrażenie w żaden sposób nie dobiera się do zawartości pliku, a zwraca jego adres, np.: appres://blobmanager/jakisGUID

Aby taki plik przekazać do Flow, należy skorzystać z pomocy Azure Blob Storage i sposobu opisanego moim poście dot. umieszczania na dokumencie ręcznego podpisu:

  1. wgranie pliku do Azure Blob Storage
  2. wywołanie Flow przekazując adres wgranego pliku
  3. odczytanie pliku z Blob przez Flow.
AzureBlobStorage.CreateBlockBlob(
    "files",
    FileUpload.FileName,
    FileUpload.Media
);

Microsoft Flow

Następnie aplikacja musi uruchomić Flow, przekazując jej adres pliku w blobie. Następnie Flow pobiera plik i w moim przypadku, jego zawartość konwertuje na JSON, wpierw usuwając linię definiującą, że jest to XML:

Getting file from Azure Blob Storage and converting its contents to JSON
Pobieranie pliku z Azure Blob Storage i transformacja do JSON

Wybrałem takie podejście, ponieważ użycie xpath do czytania pliku na pewną wadę – tworzy listę. Finalnie, by taką listę zwrócić do PowerApps i tak należy ją zamienić na JSON. A ponadto, jeśli ma ponad 5k elementówm należy dodatkowo obsłużyć limit obrotów pętli.

Aktualne ograniczenia Flow umożliwiają pętli dokonać max. 5.000 obrotów. 100.000 w planach premium.

https://docs.microsoft.com/en-us/flow/limits-and-config#looping-and-debatching-limits

I to w zasadzie wszystko. Mając tak zbudowany obiekt, w którym powtarzają mi się rekordy, mogę je zwrócić do PowerApps. Co istotne, do zwrócenia tychże rekordów standardowa akcja „Respond to PowerApps” nie wystarcza, konieczne jest użycie akcji „HTTP response”, aby przekazane dany były w formacie JSON.

Zwracam jednak nie całą zawartość pliku, a jedynie zawartość z atrybutu kodu JSON, któy w moim przypadku jest tabelą:

body('Convert_XML_to_JSON')?['employees']?['employee']
PowerApps response
Odpowiedź zwracana do PowerApps

Jak to działa?

Przede wszystkim – szybko. Kompletne wyrażenie pod przyciskiem importowania danych wygląda następująco:

AzureBlobStorage.CreateBlockBlob(
    "files",
    FileUpload.FileName,
    FileUpload.Media
);
ClearCollect(xml, ParseXMLFile.Run("/files/" & FileUpload.FileName))
PowerApp importing 500 and 5000 records from XML
Aplikacja działająca dla 500 i 5000 rekordów

Plik XML ma następujący format:

XML file being imported
XML który jest importowany

Static vs. dynamic

Faktycznie, operując na pliku XML i xpath możnaby zrobić to rozwiązanie nieco bardziej dynamicznym. W moim wypadku Flow musi znać schemat JSON, by móc prawidłowo przetworzyć dane z XML.

Można jednak przygotować własny mechanizm działający jako SaaS, który otrzymuje kod JSON i zwraca dla niego schemat, który potem zostaje użyty w przepływie.

Można też zdefiniować słownik schematów dla rodzajów XML, jakie zdecydujemy się importować do aplikacji i na podstawie wyboru użytkownika po prostu wysyłać parametr umożliwiający Flow pobranie odpowiedniego schematu.

Mam nadzieję, że opisane rozwiązanie będzie dla Ciebie pomocne. Jeśli podobał Ci się artykuł, zostaw komentarz!