In this article in the Web Components Techniques series I'm going to look at a fairly specialised topic, that of creating components that can call into VBCS Service Endpoints (REST Services managed by Oracle Visual Builder Cloud Service).
Now as a matter of course we try and build components which are totally agnostic of where they will be used, I should be able to use that component both within JET or any JET based UI, including VBCS of course. As such we might define our component API with properties which work with generic data-types such as Array or the JET DataProvider interface. However, there are cases where this is really not sufficient. For example, you are building a complex CRUD (CReate Update Delete) screen as a re-usable component. This will need to keep communicating with the appropriate REST endpoints within the context of the component, rather than just manipulating a set of data and allowing the hosting application to take care of the REST part (Note: this is just a pattern, not the pattern there are various ways for components to behave in these scenarios, there is no one-size fits all approach).
So how could we accomplish this? Well of course you could have the component take a URL to the endpoint as a property and then it can make the XMLHTTP requests internally, but the problem with that approach is that your component becomes responsible for managing any headers and security tokens that might be required, and, as it will be making a direct connection to the endpoints there may be more CORS configuration to be done on the server end to allow that communication. We also have to consider little inconveniences such as the fact that the endpoints may move between testing and production. In this article, however, we'll be looking at a more satisfactory approach where we get all the flexibility of being able to re-use existing REST endpoints that VBCS has defined already, combined with the ability to make as many calls as required to those endpoints throughout the components operational lifecycle. Because the REST service definition and communication is managed by VBCS we can safely ignore security and endpoint relocation issues because VBCS will be handling all that for us.
So in this article I'll be using the VBCS REST Helper API. Internally this is the same API that the VBCS runtime uses for it's REST calling actions and the Service Data Provider variable types. It provides a simple way to call a REST service using the abstract service and endpoint name to reference it. The precise details of the authentication and actual URL of the endpoint are hidden from us behind that service definition so the code can remain clean and does not have to be cluttered up with concerns such as setting security tokens or having URLs move over time.
The illustrate this I'll use a very simple REST API provided by GITHub - https://api.github.com/licenses/{license} (you can try this in your browser using this to see what it returns). What I'll do is create a simple component that makes a call to this API to get the license text detail for a given license name.
The first thing that we have to do of course is to define the above endpoint. I've chosen a service here that you'll all be able to get to and one which does not require any API Keys or other authorisation, just to keep things simple and easy for you to have a go at.
We start by defining a new Service Connection:
Choose Define By Endpoint:
And define the service + endpoint:
And press Next:
Here of course we would usually define the authentication as well, however, in this simple example we can skip that. However, do pay attention to the Service ID field. You will need this ID later when you come to call the service. You can change the value of you wish or use the default ID as generated. I'll use the default value of apiGithubCom here.
Next we go the the Test tab and make sure that we're getting the data we expect with an example license e.g. MIT:
At this point we press the Copy to Response Body button to describe the basic shape of the response payload and we're done so we can press the Create button.
Once the create is done, re-select the service and endpoint and make a note of the Endpoint ID as shown here. Again you can change this if you wish and I've updated mine from the default value of "getLicensesLicense" to the more readable value of getLicense.
This is the step that developers often forget and in doing so spend a lot of time trying to work out why the REST Helper is not working. In Step 1, I just defined the service and the endpoint that I needed. I did not, however, associate it with the application that I want to use it from. Remember that in a VBCS project you might have several Web and Mobile applications, so there is no assumption that every service will be used in every project. Now normally, if you are using a given endpoint in the default manner, e.g. you create a Service Data Provider that uses it, or you create an Action Chain that calls it, then VBCS design time will automatically map the service as being used in the process. However, if you're only using this endpoint programatically then you have to ensure this mapping yourself. There are two ways of doing this:
If in doubt, use the first technique, you only have to do it once for a project to get the mapping defined. The result (and the information that you would add manually if taking approach 2) is:
"services": {
"apiGithubCom": {
"path": "../../services/apiGithubCom/service.json"
}
},
Notice how the Service ID we noted earlier is mapped to its underlying definition.
Now all that pre-amble is complete I can actually get to some code. At this point I'm assuming that you have a component already and as part of that component implementation you want to call the endpoint defined earlier. Let's break this down into sub-steps:
You might not choose to do this, but I'd never want to hard-code any kind of connection information into my component so I'll add some properties with defaults to define the name of the service and endpoints that are going to be used:
{
"name": "demo-license-viewer",
"displayName": "View License",
"description": "Allows the user to select a license and view the full text for it",
"version": "1.0.0",
"jetVersion": ">=5.2.0 < 7.0.0",
"properties": {
"license":{
"displayName":"License",
"description":"Short code for the license to display",
"type":"string"
},
"serviceId":{
"displayName":"Service ID",
"type":"string",
"value":"apiGithubCom"
},
"endpointId":{
"displayName":"Endpoint ID",
"type":"string",
"value":"getLicense"
}
}
}
Next we need to make the helper code available in the component viewModel. To do this you import the vb/helpers/rest module via the define() block of your component viewModel:
define([
'knockout',
'ojs/ojcore',
'vb/helpers/rest'
], function (
ko,
oj,
RestHelper
) {
In this case, the module has been made available via the RestHelper argument to the module function, making it available throughout the component. This is the point of no-return, your component can only ever be loaded in VBCS now (unless you mock that module).
In order to start to configure and call the required endpoint we need to call the get() static function on the REST Helper passing the IDs of the service and endpoint concatenated together as a string using '/'. e.g. my endpoint would ultimately be apiGithubCom/getLicense.
In my sample here, I'm going to use the properties that I configured on the API to construct this:
self.endpointAddress = self.properties.serviceId + '/' + self.properties.endpointId;
var ep = RestHelper.get(self.endpointAddress);
In this sample we are just doing a simple GET request, but we will still need to pass one parameter, that of the license code that is being looked up, e.g. MIT. If you recall from Step 1, where I defined the endpoint, in this case this was a path parameter called license (as defined by the token in curly braces in license/{license}. However, we do the same thing with query parameters as well, which is to construct an object with the required attributes matching the parameter names and pass those to the parameters() function on the endpoint object that was received from the helper.
ep.parameters({"license":self.properties.license});
This call to parameters() can of course include as many attributes as are required to configure the call
Everything is now set and you just need to call the fetch() method on the endpoint to execute it. This will return a promise that will be resolved once the REST call is complete and the response is passed back as an object for you to parse. This returned object contains a body attribute and a response attribute which includes the following key attributes:
bodyUsed | Simple boolean flag to indicate if there is a body payload or not |
ok | Boolean flag to indicate the overall state of the call |
status | HTTP response code for the call |
statusText | HTTP response text for the call |
redirected | Boolean to indicate if the request was redirected |
So, in my example, the text of the license is returned by the GITHub API in an attribute called body within the response body (a little confusing but that's just that particular API):
ep.fetch().then(function(result){
if (result.response.ok){
//update the observable that is bound in the UI
self.licenseText(result.body.body);
}
else {
//report the error!
}
});
In the simple example I used here, I used a GET endpoint. However, we can use the REST Helper API with other endpoint types just as POST and DELETE as well. Everything is handled in the same way, and the fetch() method call is used to "execute" the REST call. The main addition that you'll need to know about for POSTs in particular, is how to set the body of the request. Well, not surprisingly, you call a function called body() in the endpoint, passing it an object which defines the payload you need to send.
There are some more advanced parts of the API for dealing with transformations, but those are for discussion in a further article.
The VBCS REST Helper API, as we've seen; is a powerful, simple to use and all-in-all valuable tool for the custom component developer, but should only be selected if you're sure it's the only way and / or your component will only be ever used from within VBCS where the API is available.
If you've just arrived at Custom JET Components and would like to learn more, then you can access the whole series of articles on the topic from the Custom JET Component Architecture Learning Path