Viime viikolla toteutettiin Power Apps, joka esitää aikajanalla käyttäjien tulevat syntymäpäivät. Ratkaisussa oli kuitenkin yksi ongelma. Syntymäpäivät haetaan yksi kerrallaan, jolloin 20 käyttäjän organisaatiossa Power Apps käynnistyy 5 sekuntia.

En halua tietää, kauanko kyseinen Power Apps käynnistyisi 1000 hengen organisaatiossa.

Rakennetaankin flow, joka hakee syntymäpäivät etukäteen Power Appsin käyttöön. Näin sovelluksen avautuminen on salamannopeaa.

Tehdään tästä hieman mielenkiintoisempaa ja hyödynnetään Graph API:n tarjoamaa delta-kyselyä. Sen avulla voimme hakea edellisen flow’n suorituskerran jälkeen käyttäjiin tehdyt muutokset!

Flow

Tehtävä flow on lyhyesti seuraavanlainen

  • Haetaan käyttäjät
  • Haetaan kaikille käyttäjille syntymäpäivätieto
  • Tallennetaan Power Appsia varten käyttäjät, joille löytyi syntymäpäivä
  • Tallennetaan tulevia suorituksia varten käyttäjät, joille ei löytynyt syntymäpäivää

Käyttäjiä voi tulla uusia ja vanhat käyttäjät saattavat käydä lisäämässä itselleen syntymäpäivätiedon.

Delta-kysely

Ennen aloitusta kerrataan lyhyesti, miten Graph API:n delta-kysely toimii.

Haetaan käyttäjät normaaliin tapaan, mutta lisätään osoitteeseen taikasana ”delta”.

https://graph.microsoft.com/v1.0/users/delta

Mikäli kaikki käyttäjät eivät mahdu yhteen vastaussanomaan, on sanomassa nextLink-osoite. Sitä kutsumalla haetaan seuraava nippu haun tuloksesta.

Viimeisessä vastaussanomanssa ei ole nextLink-linkkiä, mutta siitä löytyy deltaLink-osoite.

DeltaLink:in kutsuminen palauttaa ainoastaan käyttäjät, jotka on luotu, poistettu tai muokattu alkuperäisen kyselyn jälkeen. Viesti sisältää myös uuden deltaLink:in.

Käyttäjiä haettaessa deltaLink on voimassa aina 7 vuorokautta.

Flow’n käynnistys ja muuttujien alustus

Flow käynnistyy ajastetusti kerran vuorokaudessa. Ensimmäisenä alustamme seuraavat muuttujat.

  • Users (array) – Käyttäjät, joiden syntymäpäivät tulee selvittää
  • UserDetails (array) – Edellinen täydennettynä syntymäpäivillä
  • Removed users (array) – Organisaatiosta poistuneet käyttäjät
  • RequestURL (string) – Osoite, jota käytetään flow’n ensimmäisellä suorituskerralla

Käyttäjien haku

Seuraavaksi haemme käyttäjiä, kunnes paluusanoma ei sisällä nextLink-linkkiä. Ensimmäinen kutsu tehdään requestURL-muuttujaan juuri asettamaamme osoitteeseen.

https://graph.microsoft.com/v1.0/users/delta/?$select=userPrincipalName,userType,surname

Delta-kysely, joka palauttaa ainoastaan tarvitsemamme tiedot.

Kysely tehdään Invoke an HTTP request -toiminnolla ja sen paluuarvo parsitaan Parse JSON -toiminnolla.

Vastauksessa on ympäristön kaikki käyttäjät. Kaikki. Meitä kiinnostaa ainoastaan todelliset (sisäiset) käyttäjät, joten suodatetaan (filter) ne esiin tulosjoukosta. Ehtona käytämme seuraavia

  • UserType = Member
  • Sukunimi ei saa olla tyhjä
  • userPrincipalName ei saa sisältää sanaa ”admin”

Eli

@and(equals(item()?['userType'], 'Member'),not(equals(item()?['surname'], null )),not(contains(item()?['userPrincipalName'], 'admin')) )

Lisätään suodatettu käyttäjäjoukko users-muuttujaan. Tehdään se union-komennolla. Välissä joutuu käyttämään compose-toimintoa, sillä muuttujuaan ei voi asettaa union-toiminnolla arvoa, jossa yhdistetään jotain muuttujaan itseensä.

Päivitetään silmukan lopussa requestURL-muuttujan arvoksi uusi nextLink-osoite.

Läpikäytävät käyttäjät ovat nyt users-muuttujassa. Tehdään siitä select-toiminnolla joukko, joka sisältää vain käyttäjän id:n.

Kaiken tämän jälkeen, meillä on käyttäjien id:t sisältävä taulukko (array).

Syntymäpäivien haku

Seuraavaksi käydään tämä taulukko läpi ja yritetään hakea kunkin käyttäjän syntymäpäivä.

https://graph.microsoft.com/v1.0/users/@{items('Apply_to_each_-_New_user')?['id']}/?$select=birthday,id

Lisätään löytyneet tiedot muuttujaan (UserDetails). Kutsu menee virheeseen, mikäli etsittävää käyttäjää ei ole AAD:ssa. Lisätään tällöin käyttäjä RemovedUsers-muuttujaan.

Kaikille käyttäjille löytyy syntymäaika. Mikäli syntymäaikaa ei oikeasti ole lisätty, on sen arvo 1. Tammikuuta vuonna 1. Kätevää.

Suodatetaan UserDetails -taulukosta käyttäjät, joiden syntymäaika ei ole vuosi 1. Muodostetaan tuloksesta select-toiminnolla uusi joukko, joka sisältää käyttäjän tunnisteen ja syntymäpäivän.

Tehdään vastaava käyttäjille, joiden syntymäaikaa ei ole tallennettu.

Olemme keränneet haluamamme tiedot. Ne pitää vielä tallentaa jonnekin.

Tietojen tallennus (SharePoint)

Luodaan SharePoint-lista, jossa on kolme saraketta

  • Title – Milloin flow on suoritettu
  • JSON – Käyttäjät ja heidän syntymäpäivänsä
  • JSON users without birthday information – Käyttäjät, joille ei löytynyt syntymäpäivää

Luodaan listalle tyhjä rivi. Flow päivittää aina tätä yhtä ja samaa riviä.

Lisätään flow’n loppuun toiminto, joka tallentaa SharePoint-listalle keräämämme tiedot. Sekä DeltaLink:in, jota käytetään seuraavalla suorituskerralla.

Ai niin. Se Delta-kysely..

Ensimmäinen flow’n suoritus vs päivitykset

Flow’ta ajetaan kahdella tapaa

  • Ensimmäisellä kerralla käydään läpi kaikki käyttäjät
  • Tämän jälkeen haetaan vain käyttäjien päivitykset (deltaLink). Uudet käyttäjät lisätään aiemmin löydettyihin käyttäjiin, joilta uupuu syntymäpäivä. Löytyneet syntymäpäivät lisätään aiemmin löytyneisiin syntymäpäiviin.

Flow’hun tulee tehdä vielä hieman muutoksia.

Lisätään alkuun muuttuja (isDeltaQuery), joka kertoo olemmeko hakemassa vain päivityksiä. Se saa arvon false.

Tämän jälkeen haetaan SharePoint-listalta edellisen ajon tallentamat tiedot. Mikäli riviltä löytyy deltaLink, asetetaan se requestURL-muuttujan arvoksi. Samalla isDeltaQuery saa arvokseen true.

Jos tyhjennämme SharePoint-listan rivin arvot, aloittaa flow keräämään tietoja alusta.

Vielä pari muutosta.

Käyttäjiä läpikäydessä haluamme käydä läpi myös aiemmin löytyneet käyttäjät (joille ei löytynyt syntymäpäivää). Liitetään heidät mukaan union-komennolla.

if(equals(variables('isDeltaQuery'), bool(true)),
    union(
       body('Select_-_Users_with_only_id_info'),
       json(outputs('Get_item_-_Get_current_user_data')?['body/JSONusersmissingbirthdayinfo'])),
    body('Select_-_Users_with_only_id_info')
)

Löydetyt syntympäivät liitetään lopussa aiemmin löydettyihin syntymäpäiviin.

if(equals(variables('isDeltaQuery'), bool(true)),
    union(
       body('Select_-_Users_with_birthday'),
       json(outputs('Get_item_-_Get_current_user_data')?['body/JSON'])),
    body('Select_-_Users_with_birthday')
)

Valmis flow

Flow näyttää kokonaisuudessaan seuraavalta.

Nopeutuiko Power Apps?

Vaihdetaan viime jutussa tekemämme Power Apps käyttämään valmiiksi muodostettua JSON:ia SharePointista.

Ja testataan.

Alkuperäisellä tavalla (tiedot haetaan Power Appsissa), kahdenkymmenen käyttäjän haku kestää ~4-5 sekuntia.

SharePoint-listalta haettaessa haku kestää ~150-200 millisekuntia.

Merkittävä parannus.

Huomioitavaa

Ratkaisussa on edelleen muutama rajoittava tekijä.

  • SharePoint-listan monirivinen tekstikenttä voi sisältää oletuksena ainoastaan 63999 merkkiä. Se rajaa tallennettujen JSON-sisältöjen kokoa.
  • Flow’n muuttujaan voi tallentaa maksimissaan 104857600 merkkiä
  • Syntymäpäiviä on pakko edelleen manipuloida Power Appsissa, jolloin ne luetaan kokoelmaan. Johon voi kerrallaan lukea vain 2000 riviä.

Ratkeaako kaikki Dataversen käytöllä?

Entä jos meillä olisi käytössämme Dataverse. Muuttuuko peli?

Luodaan Dataverseen syntymäpäivät-taulu, johon tallennetaan

  • User ID – Käyttäjän tunniste
  • Birthday – Haettu syntymäpäivä (voi olla vuotena 1)
  • IsBirthdayFound – Onko syntymäpäivä oikea

Kun käyttäjät ovat Dataversessä omilla riveillään, on flow’n tekeminen helpompaa. Käyttäjiä on nyt myös helppo tarvittaessa poistaaa. Saatoit huomata että sivuutimme sen äsken kokonaan.

Tehdään kaavasarake (Birthday for App), johon lasketaan Power Appsin tarvitsema syntymäpäivä. Mikäli syntymäpäivä on tältä vuodelta mennyt, lisätään päivään vuosi. Muutoin se asetetaan kuluvalle vuodelle.

Nyt voimme järjestää syntymäpäivän omaavat rivit tämän lasketun syntymäpäivän perusteella. Ja käyttää Power Appsissa tätä.

Nopeampa ja helpompaa. Muttei tämä kaikkea ratkaise. Joudumme edelleen latamaan PowerAppsissa syntymäpäivät kokoelmaan, jotta saamme tehtyä galleriaan haluamamme väliotsikot kullekin päivälle.