To build functional and performant mobile apps, the back-end data services need to be optimized for mobile consumption. RESTful web services using JSON as payload format are widely considered as the best architectural choice for integration between mobile apps and back-end systems. At the same time, most existing enterprise back-end systems provide a SOAP-based web service application programming interface (API) or proprietary file-based interfaces. In this article series we will discuss how Oracle Service Bus (OSB) 12c can be used to transform these enterprise system interfaces into a mobile-optimized REST-JSON API. This architecture layer is sometimes referred to as Mobile Oriented Architecture (MOA) or Mobile Service Oriented Architecture (MOSOA). A-Team has been working on a number of projects with OSB 12c to build this architecture layer. We will explain step-by-step how to build this layer, and we will share tips, lessons learned and best practices we discovered along the way.
In part 1 we discussed the design of the REST API, in part 2 we discussed the implementation of the "read" (GET) RESTful services in service bus by transforming ADF BC SDO SOAP service methods. In this third part, we will implement the "expandDetails" query parameter in the /departments GET resource that we discussed in part 1 and we will implement the POST and PUT methods to create and update a department.
Before we continue with the implementation of the Rest API, it is time to revisit the XML Schema that we created for the HR REST Proxy service.The HRRestProxy.xsd contains two elements DepartmentListResponse and DepartmentDetailsResponse. Both elements contain a (partially overlapping) list of department attributes, and we need to prevent further duplication of department attributes while implementing the remainder of the HR REST API.
We can do this by specifying two complex types that hold the attributes for employee and department, and then reference these types in the element definitions rather than defining anonymous complex types within each element as we did before. Here is the refactored body of HRRestProxy.xsd now using the "named" complex types:
<xsd:element name="DepartmentListResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="departments" type="DepartmentType" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="DepartmentDetailsResponse" type="DepartmentType"/> <xsd:complexType name="DepartmentType"> <xsd:sequence> <xsd:element name="id" type="xsd:integer"/> <xsd:element name="name" type="xsd:string"/> <xsd:element name="locationId" type="xsd:integer"/> <xsd:element name="locationName" type="xsd:string"/> <xsd:element name="managerId" type="xsd:integer"/> <xsd:element name="managerName" type="xsd:string"/> <xsd:element name="employees" type="EmployeeType" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="EmployeeType"> <xsd:sequence> <xsd:element name="employeeId" type="xsd:integer"/> <xsd:element name="firstName" type="xsd:string"/> <xsd:element name="lastName" type="xsd:string"/> </xsd:sequence> </xsd:complexType>
We left the element names unchanged. If you have a need to change the element names as well, you need to modify the source of the corresponding XQuery transformations to return the correct element name and you need to redefine (remove and re-add) the REST operation binding since it is not possible to change the request or response element name for a REST operation binding once it has been created.
Also note that the DepartmentListResponse element now contains all department attributes as well as the child employees through the reference to DepartmentType. This is not a problem since these additional attributes are not mapped in the DepartmentListBS2PS XQuery transformation, so the /departments resource will still only return department id and name in the JSON payload.
With the refactored XML Schema, we can now easily make a new XQuery transformation that will return all department attributes as well as the child employees when the expandDetails query parameter is set to true, The easiest way to do this is to copy the existing DepartmentListBS2PS transformation since the input and output elements are the same. So, we select the DepartmentListBS2PS.qry transformation and choose File -> Save As... from the menu to create a new file named DepartmentListBS2PSExpanded.qry. In the visual mapper, we then can map the remaining department attributes and child employees, as shown below:
In the HR pipeline we need to conditionally use either the DepartmentListBS2PS.qry transformation or the DepartmentListBS2PSExpanded.qry transformation depending on the value of the expandDetails query parameter. To do this, we first store the value of this query parameter in a local variable using an Assign operation. We drag and drop the Assign operation from the component palette onto the circle just below the Request Action of the getDepartmentList branch. In the expression builder dialog, we select the expandDetails query parameter value:
Note how the expression is enclosed within the data() expression to get the actual value of the XML parameter node instead of the XML node. In the Assign - Properties tab, we set the variable name to expandDetails.
To use the correct transformation based on the query parameter value, we drag and drop the If Then operation onto the circle just below the Response Action of the getDepartmentList branch. We move the existing Replace action (using drag and drop) which uses the simple DepartmentListBS2PS.qry transformation to the else branch. The pipeline diagram now looks like this:
The next step is to set the Condition expression on the If - Properties tab to 'true'=$expandDetails.
The dollar sign is used to reference the expandDetails variable that we set using the Assign operation that we defined before.
Finally, we drag and drop a new Replace action underneath the if branch and set the Value property to use the DepartmentListBS2PSExpanded.qry transformation.
That's it, if we redeploy and test the resource in Postman with the expandDetails query parameter set to true, the result looks like this:
Implementing the Create (POST) and Update (PUT) resources is straightforward, it is quite similar to the implementation of the GET resources as explained in part 2, the only difference is that this time we also need to specify an XML schema element that represents the structure of the request payload, not just the response payload. We could reuse the DepartmentDetailsResponse element for this, however that would be a somewhat confusing name, so we add another element DepartmentRequest to the HRRestProxy.xsd that we will use for the POST and PUT resources:
<xsd:element name="DepartmentRequest" type="DepartmentType"/>
We can now create a new createDepartment REST binding that uses the same /departments resource, with the HTTP Verb set to POST and the request Element name set to DepartmentRequest.
When creating or updating REST resources it is good practice to return the full resource, allowing the consumer to directly access any attribute values that might have been set or updated server-side. So, on the Response tab, we select the DepartmentDetailsResponse element. We repeat these steps to create an updateDepartment binding, using the same resource and same XML elements, only the HTTP Verb is set to PUT. The Edit REST Bindings dialog now looks like this:
In the HR pipeline diagram, we add two more branches for the createDepartment and updateDepartment REST operations, and add a Route Node and a Routing for each operation,wiring up both Routing elements to the mergeDepartments operation in the ADF BC SOAP Service. We could have used the separate createDepartments and updateDepartments operations from the SOAP service, but then the request and response elements in the SOAP service would have been different, preventing re-use of the XQuery transformations that we are going to make next.
We need two more XQuery transformations, one from the incoming proxy service holding the new or updated department element, and one that returns the new or updated department element from the business service. Since we can reuse the incoming and outgoing transformations with both operations, we name them MergeDepartmentPS2BS.qry and MergeDepartmentBS2PS.qry. The "PS2BS" version uses the DepartmentRequest element from the HRRestProxy.xsd as input parameter type and the mergeDepartments element from the appropriate business service XSD.
In the visual mapper of the XQuery file we set up the SOAP mergeDepartments request body:
Likewise, we create the MergeDepartmentBS2PS.qry transformation by selecting the business service mergeDepartmentsReponse element as input and the DepartmentDetailsResponse element from the HRRestProxy schema as target element.
With the transformations in place, we are ready for the last step which is to add the Replace action in the Request Action and Response Action of both operation branches. This should feel familiar by now so we omit the detailed steps to configure these actions with the correct transformations and input parameters.
We can use the Postman REST client again to conveniently test our /departments POST resource to create a new department:
You might wonder what happens when you try to create or update a department with an invalid payload, for example a non-existing location id. Well, by default you will get a nasty internal server error as shown below.
In the next part of this article series we will discuss how you can handle such exceptions as we will look into troubleshooting and exception handling techniques in a broader sense.