Do you wanna build a snowman? (pt. 1)

Blog
02 June 2022

Joost van Dam

Hoe maak je een custom connector voor Power Apps?

Wat te doen wanneer Power Apps tekort schiet? Een verhaal over sneeuwpoppen.

Het Microsoft Power Platform heeft het maken van business apps enorm versimpeld. Power Apps biedt een breed scala aan mogelijkheden die in vele vraagstukken een oplossing kunnen bieden. Echter biedt het platform niet voor álle uitdagingen de middelen om tot een oplossing te komen. Een voorbeeld hiervan is het automatisch genereren van een (technisch) schema om deze vervolgens als afbeelding beschikbaar te maken. In dit soort gevallen maken we vaak gebruik van een zogenaamde Custom connector. We doen dit door in Microsoft Azure een zogenaamde Application Programming Interface (of API) te ontwikkelen die de technische schema’s kan genereren.

Om dit technische onderwerp enigszins luchtig te houden, illustreren we dit door een sneeuwpop te genereren. In deel #1 van deze blog bouwen we een API die een sneeuwpop kan genereren. In deel #2 integreren we deze API binnen een Power App waarmee we de sneeuwpop configureren en het resultaat tonen. We laten zo zien hoe je een duurzame modulaire oplossing bouwt die zoveel mogelijk low code is. Dus: do you wanna build a snowman?

First things first

Je vraagt je misschien af: waarom een sneeuwpop? Hoewel de winter pas net voorbij is heb ik de sneeuwdagen gemist. Het is daarnaast lang geleden dat ik een sneeuwpop gemaakt heb. Tenslotte is een sneeuwpop goed op te delen als een ‘schema’, bestaande uit gestapelde onderdelen zoals een hoed, een neus en knopen.

De opbouw van de API

We beginnen met het opstellen van een definitie van de sneeuwpop: hoe ziet de data eruit die tussen de Power App en de te bouwen API in Azure uitgewisseld wordt? Vervolgens bekijken we hoe die data omgezet kan worden naar instructies voor het genereren van de afbeelding. Tenslotte sluiten we af met het daadwerkelijk genereren van de afbeelding.

Het datamodel

Het datamodel is de definitie waarmee de generator begrijpt wat de buitenwereld ervan verwacht. Daarnaast is het voor ons een manier om de gebruiker keuzes te laten maken. Het beantwoordt vragen als hoe groot de sneeuwpop moet zijn of welke kleur de neus is (oranje natuurlijk). Voor onze sneeuwpop zijn de volgende regels van toepassing:

  • Een sneeuwpop bestaat uit drie type onderdelen: een basis, een lichaam en een hoofd;
  • Het lichaam kan uit meerdere lichaamsdelen bestaan;
  • De basis en het hoofd zijn een speciaal lichaamsdeel;
  • Ieder lichaamsdeel heeft een radius;
  • De basis kan knopen en/of voeten hebben;
  • Het hoofd kan een mond en/of een hoed hebben;
  • Standaardlichaamsdelen kunnen knopen en/of armen hebben.

Een veel gebruikte standaard voor het uitwisselen van data tussen API’s is het JSON-format (lees hier meer). Hieronder zie je hoe een sneeuwpop in dit format uitgedrukt kan worden, met bovenstaande regels in het achterhoofd:

{
      "snowman": {
            "bottom": {
                  "buttons": true,
                  "feet": true,
                  "radius": 100
            },
            "body": [
                  {
                  "buttons": true,
                  "arms": true,
                  "radius": 50
                  }
            ],
            "head": {
                  "mouth": true,
                  "hat": true,
                  "radius": 30
            }
      }
}

Het datamodel omzetten naar instructies

De service Azure Functions biedt een bijzonder goedkope en schaalbare manier om kleine applicaties efficiënt binnen Microsoft Azure te hosten. We gebruiken dit dan ook om een kleine applicatie te bouwen die de sneeuwpop voor ons genereert. Allereerst transformeren we het datamodel naar instructies voor de afbeelding generator.

Dit doen we door ieder onderdeel uit het datamodel om te zetten naar een bijbehorende klasse. Dit is een abstracte omschrijving van de mogelijkheden en eigenschappen die het onderdeel heeft in de code. Iedere klasse is een afleiding van de klasse “SnowmanPart”, waarin eigenschappen en mogelijkheden die alle onderdelen gemeen hebben staan. Doorgaans visualiseren we deze klassenstructuur door middel van een klassendiagram, zoals hieronder is te zien.

Het klasssendiagram

Dit doen we door ieder onderdeel uit het datamodel om te zetten naar een bijbehorende klasse. Dit is een abstracte omschrijving van de mogelijkheden en eigenschappen die het onderdeel heeft in de code. Iedere klasse is een afleiding van de klasse “SnowmanPart”, waarin eigenschappen en mogelijkheden die alle onderdelen gemeen hebben staan. Doorgaans visualiseren we deze klassenstructuur door middel van een klassendiagram, zoals hieronder is te zien.

In de klasse “SnowmanPart” is de basis gedefinieerd van het omzetten van het datamodel naar een instructie. Laten we in de simpelste duiken, een lichaamsdeel:

public virtual SnowmanPart Render(int verticalOffset, int width)
{
      var centerX = width / 2;
      // 1. Bepaal het midden
      var centerY = verticalOffset - Radius.Value;
      var center = new PointF(centerX, centerY);

      var shape = new EllipsePolygon(center, Radius.Value);
      // 2. Definieer de vorm en kleuren
      var fill = Color.White;
      var border = Color.Black;
      var borderThickness = 2;
      var index = 1; // determines the layer to draw on

      // 3. Bouw de instructie
      var instruction = new RenderInstruction(shape,
            fill, border, borderThickness, index);
      // 4. Voeg de instructie toe aan de lijst met instructies voor dit onderdeel
      parts.Add(instruction);
      return this;
}

Door de instructies in een lijst te stoppen kunnen afgeleide klassen (zoals het hoofd) instructies toevoegen voor de neus of de hoed. Dat ziet er als volgt uit:

De afbeelding genereren

Zodra de API een verzoek binnenkrijgt zet het de JSON data om in de zojuist genoemde klassen, wat een collectie objecten oplevert. Door daarna de render methode aan te roepen bouwt de collectie van objecten een collectie van renderinstructies op. Zoals te zien in de afbeelding hierboven bestaat een renderinstructie uit een vorm, een rand, een vulkleur en een index. De index bepaalt in welke laag de vorm aangemaakt moet worden. Om de instructies om te zetten in een .png gebruiken we een opensource 2d graphics library, ImageSharp.

De API beschikbaar maken

Met een methode om inkomende datamodellen van sneeuwpoppen om te zetten in .png afbeeldingen zijn we er bijna. De afbeelding moet nog teruggestuurd worden naar de aanvrager. Dit doen we door de afbeelding op te slaan in het geheugen van de Azure host, het stuk geheugen uit te lezen in een lijst van bytes, en de bytes terug te sturen. De aanvragen (bijvoorbeeld een Power App) kan deze bytes terug omzetten tot een afbeelding.
In versimpelde code ziet dat er als volgt uit:

byte[] output;
// Reserveer geheugen
using (MemoryStream stream = new())
// Maak een teken object aan
using (IDrawer drawer = _drawer())
{
            
            // Teken, en sla op in geheugen
            await drawer.Draw(drawing).SaveAsync(stream);
            // Kopieer inhoud geheugen naar bytes
            output = stream.ToArray();
}
// Stuur bytes terug en specificeer dat het om .png gaat
return new FileContentResult(output, asPNG); 

De afbeelding genereren

Zodra de API een verzoek binnenkrijgt zet het de JSON data om in de zojuist genoemde klassen, wat een collectie objecten oplevert. Door daarna de render methode aan te roepen bouwt de collectie van objecten een collectie van renderinstructies op. Zoals te zien in de afbeelding hierboven bestaat een renderinstructie uit een vorm, een rand, een vulkleur en een index. De index bepaalt in welke laag de vorm aangemaakt moet worden. Om de instructies om te zetten in een .png gebruiken we een opensource 2d graphics library, ImageSharp.

Tenslotte publiceren we de Azure Function achter een Azure API Management laag, waarin we onder andere kunnen configureren wie er op welke manier toegang kunnen krijgen tot de API.

Het resultaat
DO YOU WANNA BUILD A SNOWMAN?

Zelf proberen? Dat kan!

  1. Vraag allereerst een testsleutel aan via marketing@shared.nl;
  2. Ga naar https://snowmandemo.azurewebsites.net/api/swagger/ui;
  3. Vul de sleutel in binnen de ‘authorize’-popup;
  4. Definieer je sneeuwpop (of gebruik het voorbeeld van de sneeuwpop definiëren)
  5. Geniet van je sneeuwpop!

Take aways

Met de technologieën die ons vandaag de dag beschikbaar gesteld worden, kunnen we ontzettend veel, maar niet alles. Het Microsoft Power Platform maakt het bouwen van Businessapps een mogelijkheid voor een breder publiek. Dit heeft als keerzijde dat complexere applicaties steeds lastiger worden om te bouwen. Met maatwerk in de cloud kunnen complexe taken weggenomen worden van het Power Platform, waardoor de mogelijkheden alleen maar groeien en de complexiteit niet hoeft toe te nemen. Zo heb je de zowel de voordelen van snelle low code ontwikkeling alsook de flexibiliteit van complexer, modulair en herbruikbaar maatwerk.

Hoe cool de sneeuwpop ook is die we generen; het komt natuurlijk niet in de buurt van het gemak en het plezier van het echte ding maken. Hoe lang het duurt in Nederland tot we weer die kans krijgen durf ik niet te zeggen. Gelukkig is het gebruik van deze Cloud based ‘image creation service’ oneindig te versimpelen met het Power Platform. Hoe? In pt #2 van deze serie lopen mijn collega’s er in detail doorheen.

Contact

Je naam
This field is for validation purposes and should be left unchanged.
Shared BV

Vasteland 12G
3011 BL Rotterdam

+085 833 0011 info@shared.nl
Sluiten