Validating XML against Schema¶
In this section you start developing the adapter that the imaginary company New Horizons needs, continuing the case study started in section Example: New Horizons. The adapter will process an XML document with a valid booking, see section New Horizons Requirements for an example. It will write the booking to the database tables “booking” and “visit” you created in section Database Initialization. If you did not do the previous sections, you can
download that work and continue here from your download.
The ingest booking adapter¶
Before doing something with a booking XML, the ingest booking adapter should check that this document is valid. In this section you will write a first version of the ingest booking adapter that only does that. Please do the following:
The validity of an XML documents is usually checked using an XML schema, see https://www.w3schools.com/xml/schema_intro.asp. Please make a document
NewHorizons/booking.xsdand give it the following contents:
<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="booking"> <xs:complexType> <xs:sequence> <xs:element name="travelerId" type="xs:integer"/> <xs:element name="price" type="money"/> <xs:element name="fee" type="money"/> <xs:element name="destination" minOccurs="1" maxOccurs="unbounded" type="destination" /> </xs:sequence> <xs:attribute name="id" type="xs:integer"></xs:attribute> </xs:complexType> </xs:element> <xs:simpleType name="money"> <xs:restriction base="xs:decimal"> <xs:fractionDigits value="2" /> </xs:restriction> </xs:simpleType> <xs:complexType name="destination"> <xs:all> <xs:element name="price" type="money"/> <xs:element name="startDate" type="xs:date"/> <xs:element name="endDate" type="xs:date"/> </xs:all> <xs:attribute name="hostId" type="xs:integer"/> <xs:attribute name="productId" type="xs:integer"/> </xs:complexType> </xs:schema>
This schema does not check all possible requirements for a booking to be valid. It does not check that the start date of a visit is before its end date. More advanced checks are possible, but then you need features that are new in XML Schema version 1.1. These features are explained at https://www.altova.com/blog/what-s-new-in-xml-schema-11/. The Frank!Framework supports XML Schema 1.1, but you need a commercial text editor to use the new features. With a free text editor, you will not have syntax checking while working on your advanced XSD file.
To have syntax checking as explained at the end of section Syntax Checking and the Frank!Doc with a free text exitor, you have to stick to XML Schema version 1.0. We do so in this tutorial.
We will write our adapter in its own file that will be named
ConfigurationIngestBooking.xml. The Frank!Framework will read file
Configuration.xml, so that file needs to include
ConfigurationIngestBooking.xml. Please open
NewHorizons/Configuration.xmland update it as shown:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration [ <!ENTITY IngestBooking SYSTEM "ConfigurationIngestBooking.xml"> ]> <Configuration name="NewHorizons"> &IngestBooking; </Configuration>
Please create file
NewHorizons/ConfigurationIngestBooking.xml. Put the following contents:
<Module xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./ibisdoc.xsd"> <Adapter name="IngestBooking"> <Receiver name="input"> <ApiListener name="inputListener" uriPattern="booking" method="POST"/> </Receiver> </Adapter> </Module>
You start with a
<Module> tag. It is there to satisfy XML schema
ibisdoc.xsd, which allows your text editor to provide automatic code completion. The adapter starts with a
<Receiver> that contains an
<ApiListener>. The choice for
<ApiListener> makes the adapter listen to REST HTTP requests. The attribute
method="POST" makes it listen to HTTP POST requests. The
uriPattern="booking" attribute defines the relative path to which the adapter listens. The Frank!Framework extends this path to be http://localhost/api/booking.
The Frank!Framework defines a pipe
<XmlValidatorPipe>that checks the incoming message against an XML Schema. We use it in our adapter. Please update
<Module xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./ibisdoc.xsd"> <Adapter name="IngestBooking"> <Receiver name="input"> <ApiListener name="inputListener" uriPattern="booking" method="POST"/> </Receiver> <Pipeline firstPipe="checkInput"> <Exit path="Exit" state="success" code="201" /> <Exit path="ServerError" state="failure" code="500" /> <XmlValidatorPipe name="checkInput" root="booking" schema="booking.xsd"> <Forward name="success" path="Exit" /> <Forward name="failure" path="ServerError" /> </XmlValidatorPipe> </Pipeline> </Adapter> </Module>
schema are used to reference the expected root element of the incoming XML and to reference the XML schema file
booking.xsd presented in step 1. A
<Forward> tag links a forward name to a path. On success, we go to the pipeline exit having path
Exit, finishing execution. The
<Pipeline> tag contains an
<Exit> tag that links path
Exit to exit state
success and code
<XmlValidatorPipe> supports another forward name
failure that is followed when validation fails. It is linked to forward path “ServerError” at this point, corresponding to exit state
failure and code
<XmlValidatorPipe>echos its input message to its output message, both if validation succeeds and if validation fails. We want an error message if we receive an invalid booking message. Please update
... <XmlValidatorPipe name="checkInput" root="booking" schema="booking.xsd"> <Forward name="success" path="Exit" /> <Forward name="failure" path="makeInvalidBookingError" /> </XmlValidatorPipe> <FixedResultPipe name="makeInvalidBookingError" returnString="Input booking does not satisfy booking.xsd"> <Forward name="success" path="ServerError"/> </FixedResultPipe> </Pipeline> </Adapter> </Module>
failure is linked to the pipe named
makeInvalidBookingError. This pipe replaces the incoming message by an error message. The fixed result pipe never fails and follows its (predefined) forward name
success. That forward points to path
Your adapter listens to REST HTTP requests. If you are working under Windows, you can use Postman to send HTTP requests to your adapter. Please do the following:
Install Postman from https://www.getpostman.com/downloads/ if you do not have it.
Go to File | Settings, select tab General.
Ensure that “SSL certificate verification” is not checked, see figure below:
Close this dialog.
Select method POST (number 1 in the figure below) and type URL
Select tab “Headers” (number1 in the figure below). Add header
Content-Type(number 2) with value
application/xml(number 3) and select it (number 1):
Select tab “Body” (number 1 in the figure below).
In the message field (number 2), copy/paste the following XML:
<booking id="1"> <travelerId>2</travelerId> <price>500.00</price> <fee>100.00</fee> <destination hostId="3" productId="4"> <price>400.00</price> <startDate>2018-12-27</startDate> <endDate>2019-01-02</endDate> </destination> </booking>
Press “Send” (number 3 in the figure).
Check the response. Go to the “Body” tab (number 1 in the figure below). You should see that the response equals the original XML message (number 2). You should have status code
Under Linux, you can test your adapter as follows:
Copy the valid booking XML listed above (subsection Testing (Windows)) to some file on your computer, say
Execute the following Linux shell command:
curl -i -X POST -H 'Content-Type: application/xml' -d @validBooking.xml http://localhost/api/booking
The output will be something like the following:
HTTP/1.1 201 Created Server: Apache-Coyote/1.1 Last-Modified: Wed, 16 Oct 2019 12:39:06 GMT Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0 Pragma: no-cache Allow: OPTIONS, POST Content-Type: */*;charset=UTF-8 Content-Length: 247 Date: Wed, 16 Oct 2019 12:39:06 GMT <booking id="1"> <travelerId>2</travelerId> <price>500.00</price> <fee>100.00</fee> <destination hostId="3" productId="4"> <price>400.00</price> <startDate>2018-12-27</startDate> <endDate>2019-01-02</endDate> </destination></booking>
The HTTP status code
201 is the
code attribute defined with exit state
success. To the bottom, you see that the incoming XML is echoed in the body of the response.
Final remarks (Windows and Linux)¶
The HTTP request includes a HTTP header
Content-Type: application/xml. You need this header because the ingest booking adapter uses listener
<ApiListener>. Use another listener if you want to omit the header from the request.
The exit path
Exit corresponds to code
201 and state
success. This exit state
success does not appear in the HTTP response. You can see it if you use the “Test Pipeline” page in the console, see section Run and Debug an Adapter.
Please test your adapter with XML documents that do not satisfy
booking.xsd or with text that is not valid XML. You should see the message
Input booking does not satisfy booking.xsd and HTTP status code