Thursday, March 8, 2018

Creating REST APIs with Oracle Service Bus



When you think of Oracle Service Bus, you probably think about integration with SOAP and XML messages. However, since the introduction of REST adapters, it's also possible to offer RESTful APIs with JSON messages to your service consumers. Since RESTful APIs tend to be more light-weight than SOAP services, they have certain advantages in performance, especially for mobile usage, while also simplifying the interaction with your service. In this blog, I will show you how to create such an API based on an XSD for internal XML processing and what things to pay specific attention to. In Github I have provided a sample application created in version 12.2.1.2.0: https://github.com/lthijssen/MyMusic

Step 1: create your project and XSD

First of all, you will need to create a Service Bus Application in JDeveloper and a project within that. From there, create a Schemas folder and within that folder a MyMusic.xsd XML Schema with the following content:


As you can see, it's quite a straight-forward, simple XSD supporting the most common operations for integrating with a music database: get, getById, create, update & delete. Now, before you get overexcited and start hacking away on a WSDL, please don't! The WSDL will be automatically created later on in the process and making one manually will only hurt you. Needless to say that I found this out the hard way!
The interesting part of the XSD is that it contains some attributes in the schema declaration that you may be unfamiliar with: the nxsd parts are necessary to convert XML to JSON and vice versa later on, so they are absolutely mandatory. Also found this one out the hard way...

While normally I would create a container element Albums with an unbounded element Album in it, this is not looking pretty in JSON, so I have chosen to make Albums unbounded. If you want to know more about how XML is converted to JSON and vice versa, check the following blog by Oracle's A-team:
http://www.ateam-oracle.com/rest-adapter-and-json-translator-in-soaosb-12-1-3-2/

Another thing that I found out the hard way: don't put any "anyType" elements in your XSD. Later on, your generated WADL will fail because of this.

Step 2: the REST adapter

Once you have completed Step 1, you can go to your composite.xml, right-click on the left swimlane and choose REST. Call the REST Binding "MyMusicAPI" and tick the checkbox in front of "Service will invoke components using WSDL interfaces". Click "Next" and you will get to the Resources screen. This is where you bind your REST methods with your (generated) WSDL operations, so this one is highly important. You need two resource paths: "/" and "/{id}". These paths are coming back in the endpoints that your consumers need to invoke, so keep them as simple as possible and don't put any verbs in there, as they will be supplied by your methods already. The "/" path is allowing methods on the root, while "/{id}" is demanding an id to be sent in the URL and offering methods on that. A simple example: GET on "/" will get you all albums, while GET on "/{id}" gets you a specific album, based on the id supplied.

Now, under Operation Bindings, you start binding the REST methods on your paths to WSDL operations. Click the + button to start! In the next screen, you can set up the specific details. I will call the first operation "getAlbums", which is using the "/" resource and the "GET" HTTP Verb. Under the "Request" tab, in the schema URL, select the "getAlbumsRequest" element from your XSD. Any URI parameters that have been generated should be deleted, since the "/" resource is not having any, so they can't be bound to XML payload. In the Response tab, select "getAlbumsResponse" and leave everything else at the default setting. While it's a good practice to implement error handling, we will leave it for the next blog. Now click "OK" and you have created your first Operation Binding.

Now create "getAlbumById", which binds a GET method on resource "/{id}". Select the appropriate elements from the XSD under Request and Response and make sure to remove the second "id" parameter under URI parameters in the Request part, one is enough. The mapping to the internal XML payload is automatically created!

The next operation is "createAlbum", which is a POST method on resource "/". Remove any URI parameters in the Request and select JSON for the Response before you can select the appropriate XSD element. You can create a sample payload in the Request if you like, so you can see how your XSD content translates to JSON.

Next is "updateAlbum", which is a PUT on "/{id}". The implementation is similar to the POST we just did, but we don't remove any URI parameters here unless they're double. "deleteAlbum" can be implemented the same way.

Now you can press "Finish" and Oracle Service Bus will generate a ProxyService, a WSDL and a WADL. Look into these files and see what they contain! In your composite, you now see an errored Proxy Service, since it's not tied to anything. That's okay for now, no cause for concern. When you right-click on your Proxy Service, you can choose "Edit" and "Edit REST". The first one is the edit function that we know for Proxy Services, including Endpoint URI etc... but "Edit REST" allows you to edit the bindings and the paths exposed by the API.

Step 3: implement the Pipeline

Right-click in the middle swimlane of your composite and select "Pipeline". Call this one "MyMusicPipeline" and on the next screen select "WSDL" as service type. Here you will choose the WSDL that OSB has generated. Untick "Expose as a Proxy Service", since we already have one and we want to make a RESTful API. Now you can drag a wire between the Proxy Service and the Pipeline in the composite or edit the Proxy Service and select the Pipeline as target. In the Pipeline, you need to create an operational branch with all the operations in the WSDL. Give each operation a Pipeline Pair.

Now you can create a mock response in the Response pipelines. You can do this with a Replace activity, which gives you several XQuery and XSLT options to do so. My preference is to use XSLT resources. Why a mock response? Because we want to follow an API first pattern, which means that we deliver our API to consumers as soon as possible, so they can start working on their side and deliver feedback, while we work on the real implementation in the back end.

To create the XSLTs, I've created a Transformations folder and will make an XSL Map for each of the operations. Use the request and response elements from the XSD as sources and targets and fill in the mock response as you desire. Check the sample code in Github to see my implementation. When implementing the Replace activities, choose "body" as Location and "replace node contents" as Replace Option. For Value, you select "XSLT Resources" and select your XSL Map in the next screen, while setting Input Document Expression to $body/* and you're done.

Step 4: wrap it up and deploy

Go back to the Proxy Service and choose 'Edit'. Under "Transport", change the endpoint to /rest/albums to make it more straight-forward for the consumers. Now we're ready to deploy and test! You can deploy directly from JDeveloper, with Maven, through any continuous delivery tool, it doesn't matter to me. For this, I will just use JDeveloper and deploy to my localhost environment by right-clicking on my project, choosing deploy and following the wizard. Of course, you do need an OSB domain for this, so make sure you have. It falls out of the scope of this blog to teach you how to create one. Now you can check http://localhost:7001/servicebus to verify that "MyMusicProject" is there and start testing the Proxy Service!

Step 5: test your REST API

You are ready to test your proxy service. You can do this directly from the OSB console or from any client that can handle REST, like SoapUI/ReadyAPI, curl or Postman. For GET methods, even your browser will work! In this case, I choose the OSB console, which can be accessed at http://localhost:7001/servicebus. Just open the project, select the MyMusicAPI Proxy Service and hit the green button saying "Launch Test Console".

Test 1, "GET" method on resource "/" should give us a list of albums and it does.
Test 2, "POST" method on resource "/" should create an album and it does.
Test 3, "GET" method on resource "/{id}" expects an id to be entered and then returns the album.
Test 4, "DELETE" method on resource "/{id}" expects an id to be entered and removes an album.
Test 5, "PUT" method on resource "/{id}" expects an id to be entered and gives us a nasty error message!

Step 6: fix the bug!

What happened here? Since the id element in the XSD for internal usage, it's also exposed in the API. However, we've also mapped the id from the resource path to this element, so OSB doesn't know which one we want to use: the one from the JSON payload (even if the id is empty or removed). Following RESTful standards, we will want to use one in the resource path, but how do we do that? Let's go back to JDeveloper and "Edit REST" on our Proxy Service. Select the "updateAlbum" binding and edit. Edit the "id" parameter and change its mapping to "$property.id", so it's stored as a property, instead of fighting with the id in the JSON payload.

Now go to your Pipeline and do the following:
Click on the arrow button in the left top corner of your Pipeline to see "Shared Variables" and "External Services". Right-click and create a Shared Variable called albumId. Now add an Assign activity above the Replace activity. Choose "XQuery Expression" for Value and access your property.id with the following expression:
$inbound/ctx:transport/ctx:request/tp:user-metadata[@name='id']/@value
For Variable, select the "albumId" variable that you've just created.

Now create a new Mock XSL Map for Update... you can also edit the old one, but for demonstration purposes, I've created a second one (UpdateAlbumMock2) to show the differences. Create it the same way as the other one, just add an additional source (green + button) and call it "albumId". Leave all the default settings. Now you will do your mapping to id from this parameter, not from the id in the payload. Now update the XSLT Resource in the Replace activity and bind the parameter to the albumId variable. Save and redeploy. Test your PUT method again and you'll see that it works!

Conclusion

Through this blog, you should now understand the basics of creating a RESTful API on Oracle Service Bus with JSON input and output. In my next blog, I will show you how to deal with search parameters and error handling.


No comments:

Post a Comment