fb
WSO2 Enterprise Integrator 14 minuten

Simpele applicaties maken die ‘goed genoeg’ zijn

Rob Blaauboer
Rob Blaauboer
Integration Consultant & WSO2 Trainer
Creating simple solutions that are good enough
Scroll

Tijdens de coronapandemie hebben we talloze voorbeelden van situaties gezien waarin snel handelen noodzakelijk was. Een goed voorbeeld is het moment waarop restauranthouders plotseling de deuren van hun zaak niet meer mochten openen, maar alleen nog maar afhaalmaaltijden mochten verkopen. 

Gegeven de abrupte verandering in omstandigheden die we toentertijd meemaakten, hadden we geen perfecte oplossingen nodig, maar juist iets dat ‘goed genoeg’ was. Iedere dag dat deze oplossing op zich zou laten wachten, zou omzetverlies betekenen. Hadden we meer tijd gehad, dan hadden we de oplossing zeker nog veel verder verbeterd, maar op dat moment hoefde het alleen maar te werken. 

Een specifiek voorbeeld is van een ijssalon die nu mensen de mogelijkheid moest gaan bieden om online te bestellen, zodat ze daarna de bestelling in de winkel op konden halen. Net even anders dan hoe het er normaal aan toe ging. Maar alleen op die manier kon de uitbater tenminste iets verdienen.

Snelle oplossing om klantgegevens te registreren – Microsoft 

Een poosje geleden deelde een van mijn contacten bij Microsoft op LinkedIn een voorbeeld van hoe bestaande tools en applicaties van Microsoft makkelijk gebruikt kunnen worden om klantgegevens te registreren, iets wat in deze coronatijd erg belangrijk is. Overheden besluiten dat restaurants nu gegevens van bezoekers vast moeten leggen om mogelijke verspreidingen van het virus te kunnen traceren. 

Pen en papier zijn niet afdoende

Natuurlijk is de makkelijkste variant degene die al het langste meegaat: pen en papier. Maar als je jouw gegevens digitaal opslaat, dan kun je de kwaliteit verbeteren. Bovendien kun je, met de juiste aanpak, ervoor zorgen dat de gegevens na een specifiek tijdsbestek, laten we zeggen een week of twee, verwijderd worden. We willen namelijk geen onnodige gegevens bewaren of data langer opslaan dan noodzakelijk is.

Hoe kun je met WSO2 producten snel een oplossing vinden? 

Ik schrijf deze blog om te laten zien hoe makkelijk het is om snel iets in elkaar te zetten en uit te proberen. Ik bedoel niet dat je doorlopend met een beta versie moet gaan werken, maar wil benadrukken dat zelfs als er haast bij is, het goed te doen is om iets te ontwikkelen en uit te proberen. Het is niet al te moeilijk om de PostNL API aan het systeem toe te voegen en het helpt je om je gegevens te valideren en te verbeteren. Bovendien verkort je daarmee het registratieproces. 

Een eenvoudige set-up die gegevens opslaat

Wat ik nu ga laten zien, is hoe je in enkele uren een hele eenvoudige set-up kunt maken waarmee je gegevens op kunt slaan en, tot op zekere hoogte, kunt valideren of de invoer logisch is, zoals bij postcodes. Hiervoor hebben we een simpele front-end en een aantal API’s nodig in combinatie met een eenvoudig databasemanagementsysteem, een paar opgeslagen procedures en natuurlijk de producten van WSO2. 

Enterprise Integrator

Ik ga de WSO2 Enterprise Integrator gebruiken. In dit geval neem ik de installatie met een Micro Integrator. Op die manier kan ik het in een Docker omgeving aanbieden, waardoor het beter schaalbaar is en de functionaliteit van de Enterprise Integrator benut.

MariaDB

Wat hebben we nodig om te kunnen starten? Om te beginnen hebben we zeker weten een plek nodig waar we de gegevens op gaan slaan. Ik kies in dit geval voor een databasemanagementsysteem zoals MariaDB. Dat is een eenvoudig werkend systeem met veel ingebouwde functies die je waarschijnlijk gaat gebruiken. 

Laten we beginnen met het instellen van de database. Wat we als eerste nodig hebben zijn: voornaam, achternaam, postcode, een NL telefoonnummer en een e-mailadres.Natuurlijk gaan we ook een tijdsaanduiding toevoegen. Wanneer de informatie in de database gezet wordt, dan wordt de tijdsaanduiding gebruikt om de data op enig moment op te kunnen schonen. Met opschonen bedoel ik hier vooral het voldoen aan wet- en regelgeving.

Dit is best een eenvoudige set-up die laat zien hoe snel je iets dergelijks kunt opbouwen. Ik ga een primaire sleutel aanmaken die uiteraard uniek is en automatisch oploopt. Hoe de database aangemaakt wordt, staat hieronder weergegeven.

#coronapostcode


CREATE DATABASE IF NOT EXISTS coronapostcode;
USE coronapostcode;

CREATE TABLE IF NOT EXISTS guests (
   GID INTEGER NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(40) NOT NULL,
   CELLPHONE VARCHAR(10) NOT NULL,
   ADDRESS VARCHAR (50) NOT NULL,
   HOUSENR VARCHAR (5) NOT NULL,
   POSTCODE VARCHAR(50) NOT NULL,
   TOWN VARCHAR(50) NOT NULL,
   DTSTAMP datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (GID)
);
SET insert_id=2020;

INSERT INTO guests (name,cellphone,address,housenr,postcode,town) VALUES ('Jill Doe', '0612345678','Rokin','12','1002AB','Amsterdam');

select * from guests;

Oké, het instellen van de primaire sleutel van de database is geregeld. Ik heb alvast een registratie ingevoegd om te kunnen zien of het werkt. Laten we nu de SOAP Services bekijken. Hoe dat allemaal werkt, zie je in deze video:

Het is belangrijk om je te realiseren dat het aanmaken van de zojuist gecreëerde data service minder dan twee minuten duurde. Er zit direct een SOAP interface bij die ons de mogelijkheid geeft om data in te voegen in de database. Dus met een eenvoudige front-end ontwikkeling die de SOAP service aanroept zijn we klaar om de service te gebruiken. Ik ga sla de details over de front-end ontwikkeling over, omdat dat niet binnen mijn expertise ligt en te ver afwijkt van het doel van deze blog (we richten ons hier op integratie).

De Enterprise Integrator kan ook als front dienen voor de SOAP Service met een API, dus we kunnen RESTful invocations gebruiken die moderner zijn en het makkelijker maken voor een team front-end developers.

Creëer de API

En nu moet ik natuurlijk de API maken. Waarom een API? Nou, omdat dat de natuurlijkste manier is om dit aan te pakken. Als je informatie opslaat, dan zijn API’s de gebruikelijke weg. Ze zijn simpel. Ze hebben een grote aanhang, wat ontwikkelaars betreft, en het belangrijkste is dat ze relatief eenvoudig te maken zijn. In ons geval gaan we gewoon een HTTP POST doen, waaraan we de informatie als een JSON payload meegeven, zodat die aan de database toegevoegd wordt. Ik ga de API op de Enterprise Integrator maken, omdat ik daar bepaalde bewerkingen op de payload kan uitvoeren.

Van API naar SOAP

Ook het maken van een API die communiceert met de SOAP back-end is eigenlijk vrij eenvoudig. Dit zijn de stappen: 

  1. Voeg een Header toe en stel de gepaste Action in
  2. Voeg de PayloadFactory Mediator toe
  3. Voeg een Call Mediator en een Endpoint toe
  4. Gebruik Respond om een respons terug te sturen naar de client

In de video zijn deze instellingen in de high-level structuur zien. 

De payload kunnen we bekijken als we de WSDL gebruiken in bijvoorbeeld de SoapUI.

De broncode laat de details zien. 

<?xml version="1.0" encoding="UTF-8"?>
<api context="/add" name="Customer" xmlns="http://ws.apache.org/ns/synapse">
    <resource methods="POST">
        <inSequence>
            <header name="Action" scope="default" value="insert"/>
            <payloadFactory media-type="xml">
                <format>
                    <soapenv:Envelope xmlns:dat="http://ws.wso2.org/dataservice" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                        <soapenv:Header/>
                        <soapenv:Body>
                            <dat:insert>
                                <dat:name>$1</dat:name>
                                <dat:cellphone>$2</dat:cellphone>
                                <dat:address>$3</dat:address>
                                <dat:housenr>$4</dat:housenr>
                                <dat:town>$5</dat:town>
                                <dat:postcode>$6</dat:postcode>
                            </dat:insert>
                        </soapenv:Body>
                    </soapenv:Envelope>
                </format>
                <args>
                    <arg evaluator="xml" expression="$body//name"/>
                    <arg evaluator="xml" expression="$body//cellphone"/>
                    <arg evaluator="xml" expression="$body//address"/>
                    <arg evaluator="xml" expression="$body//housenr"/>
                    <arg evaluator="xml" expression="$body//town"/>
                    <arg evaluator="xml" expression="$body//postcode"/>
                </args>
            </payloadFactory>
            <call>
                <endpoint>
                    <address format="soap11" uri="http://localhost:8280/services/Guest">
                        <suspendOnFailure>
                            <initialDuration>-1</initialDuration>
                            <progressionFactor>-1</progressionFactor>
                            <maximumDuration>0</maximumDuration>
                        </suspendOnFailure>
                        <markForSuspension>
                            <retriesBeforeSuspension>0</retriesBeforeSuspension>
                        </markForSuspension>
                    </address>
                </endpoint>
            </call>
            <respond/>
        </inSequence>
        <outSequence/>
        <faultSequence/>
    </resource>
</api>

Maar er is nog meer. We kunnen deze gegevens, die onze API binnenkomen, namelijk valideren.

Het valideren van de gegeven met de POSTNL API

Voor het valideren van de gegevens kunnen we een API gebruiken, bijvoorbeeld die van PostNL. PostNL heeft verschillende API’s om de aangeboden diensten te integreren, zoals track en trace systemen voor allerlei soorten webshops. Je kunt de API’s online vinden en daar zelf ook je eigen trial API Key aanvragen om het zelf uit te proberen. 

Strikt genomen is de oplossing die we nu hebben staan al ‘goed genoeg’. Want we slaan gegevens op in een database en we kunnen die informatie ook weer opvragen. We kunnen het onszelf nog wel makkelijker maken, omdat een combinatie van postcode en huisnummer in Nederland (bijv.: 1119 PT, 16), indien correct, het volledige adres geeft. Zodoende hoeven we alleen nog maar om de naam, telefoonnummer en postcode + huisnummer te vragen en maken we het onze klanten makkelijker door straatnaam en woonplaats automatisch aan te laten vullen.

Ik heb me aangemeld om de API voor adrescontrole in de Benelux uit te proberen. Zo kan ik de API testen. Ik heb het met SoapUI geprobeerd om te zien welke informatie ik terugkrijg voor ons eigen kantoor. Hoewel Postman beter zou passen bij API’s, gebruik ik SoapUI omdat ik het ook al gebruikt heb om het SOAP-bericht in de Payload Factory te kopiëren. Bij PostNL is de optie voor Postman collections online beschikbaar, voor als je Postman zou willen uitproberen.

Good enough solutions 1

Dit kost echter wel wat meer werk. 

We zullen het originele bericht eerst op moeten slaan (de losse velden) en de PostNL API aan moeten roepen. In de documentatie kunnen we de URL en parameters vinden. We zullen hiervoor de juiste payload moeten maken (dezelfde als die hierboven staat, de apiKey toe moeten voegen als Header en de API moeten aanroepen). De respons die we terugkrijgen geeft ons de informatie die we nodig hebben. De meeste velden in de API definitie zijn optioneel, dus je zou ook meer gegevens kunnen valideren. Meer daarover vind je in de documentatie van de PostNL API.

In deze blog ga ik ervan uit dat de ingevoerde Postcode en Huisnummer (HouseNumber) correct zijn. Normaliter zou ook daar een foutcontrole op gedaan moeten worden. Om het zo eenvoudig mogelijk te houden, sla ik dit nu over. Ik maak bewust twee fouten in de originele gegevens bij het aanroepen van de API (Sciphol en Beachavenue bevatten beide een typfout). Door de gegevens van PostNL als uitgangspunt te nemen (zij zijn de bron) kan ik altijd de invoer van de klant overschrijven om zodat de gegevens overeenkomen met de respons van de PostNL API. 

De code is eenvoudig. Dit is het grafische overzicht:

Good enough solutions 2

Dat ziet er als code zo uit:

<?xml version="1.0" encoding="UTF-8"?>
<api context="/add" name="Customer" xmlns="http://ws.apache.org/ns/synapse">
    <resource methods="POST">
        <inSequence>
            <propertyGroup>
                <property expression="$body//name" name="orgname" scope="default" type="STRING"/>
                <property expression="$body//cellphone" name="orgcellphone" scope="default" type="STRING"/>
                <property expression="$body//address" name="orgaddress" scope="default" type="STRING"/>
                <property expression="$body//housenr" name="orghousenr" scope="default" type="STRING"/>
                <property expression="$body//town" name="orgtown" scope="default" type="STRING"/>
                <property expression="$body//postcode" name="orgpostcode" scope="default" type="STRING"/>
            </propertyGroup>
            <payloadFactory media-type="json">
                <format>{
   "CountryIso": "NL", 
 "HouseNumber": "$1",
"PostalCode": "$2"
}
</format>
                <args>
                    <arg evaluator="xml" expression="$ctx:orghousenr"/>
                    <arg evaluator="xml" expression="$ctx:orgpostcode"/>
                </args>
            </payloadFactory>
            <propertyGroup>
                <property name="messageType" scope="axis2" type="STRING" value="application/json"/>
                <property name="apikey" scope="transport" type="STRING" value="**Fill in the API key you received by requesting a trial API key**"/>
            </propertyGroup>
            <call>
                <endpoint>
                    <http method="post" uri-template="https://api.postnl.nl/address/benelux/v1/validate">
                        <suspendOnFailure>
                            <initialDuration>-1</initialDuration>
                            <progressionFactor>-1</progressionFactor>
                            <maximumDuration>0</maximumDuration>
                        </suspendOnFailure>
                        <markForSuspension>
                            <retriesBeforeSuspension>0</retriesBeforeSuspension>
                        </markForSuspension>
                    </http>
                </endpoint>
            </call>
            <propertyGroup>
                <property expression="$body//City" name="City" scope="default" type="STRING"/>
            </propertyGroup>
            <filter xpath="$ctx:orgtown!=$ctx:City">
                <then>
                    <property expression="$ctx:City" name="orgtown" scope="default" type="STRING"/>
                </then>
            </filter>
            <header name="Action" scope="default" value="insert"/>
            <payloadFactory media-type="xml">
                <format>
                    <soapenv:Envelope xmlns:dat="http://ws.wso2.org/dataservice" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
                        <soapenv:Header/>
                        <soapenv:Body>
                            <dat:insert>
                                <dat:name>$1</dat:name>
                                <dat:cellphone>$2</dat:cellphone>
                                <dat:address>$3</dat:address>
                                <dat:housenr>$4</dat:housenr>
                                <dat:town>$5</dat:town>
                                <dat:postcode>$6</dat:postcode>
                            </dat:insert>
                        </soapenv:Body>
                    </soapenv:Envelope>
                </format>
                <args>
                    <arg evaluator="xml" expression="$ctx:orgname"/>
                    <arg evaluator="xml" expression="$ctx:orgcellphone"/>
                    <arg evaluator="xml" expression="$ctx:orgaddress"/>
                    <arg evaluator="xml" expression="$ctx:orghousenr"/>
                    <arg evaluator="xml" expression="$ctx:orgtown"/>
                    <arg evaluator="xml" expression="$ctx:orgpostcode"/>
                </args>
            </payloadFactory>
            <call>
                <endpoint>
                    <address format="soap11" uri="http://localhost:8280/services/Guest">
                        <suspendOnFailure>
                            <initialDuration>-1</initialDuration>
                            <progressionFactor>-1</progressionFactor>
                            <maximumDuration>0</maximumDuration>
                        </suspendOnFailure>
                        <markForSuspension>
                            <retriesBeforeSuspension>0</retriesBeforeSuspension>
                        </markForSuspension>
                    </address>
                </endpoint>
            </call>
            <payloadFactory media-type="json">
                <format>{ "record status": "added" }</format>
                <args/>
            </payloadFactory>
            <respond/>
        </inSequence>
        <outSequence/>
        <faultSequence/>
    </resource>
</api>

Een bericht naar de API sturen

We kunnen de onderstaande payload beschouwen als de gegevens die door een gast zijn ingevuld via zijn mobieltje. Ik gebruik SoapUI om het bericht na te bootsen. 

Good enough solutions 3

De data controleren

Omdat we een registratie in een database invoeren en geen andere respons hebben dan de standaard ‘202 Accepted’, zullen we zelf moeten controleren of de gegevens correct zijn opgeslagen.

Good enough solutions 4

Door een snelle select functie uit te voeren in de database (select * from guests) zien we dat de gegevens erin staan. De oplettende lezer zal opmerken dat Beachavenue niet veranderd is. Er is namelijk een extra filter statement nodig om dat te corrigeren. Of, en dat is ook iets, je kunt de gegevens die de klant opgeeft overschrijven met de response van PostNL.

Conclusie

Een applicatie maken die ‘goed genoeg’ is makkelijk te doen. Natuurlijk bevat een versimpeld voorbeeld geen front-end en mist het de standaard foutverwerking. Maar toch biedt het een basis die makkelijk uit te breiden is tot een minimaal werkbare integratie. Voor nu is het goed genoeg.

Je kunt de broncode hiervan vinden op de Yenlo Bitbucket page.