Sharing code between Worklight Adapters - ibm-mobilefirst

In most cases I've dealt with so far the Worklight Adapter implementation has been pretty trivial, just a few lines of JavaScript.
On the current project, using WL 5.0.6, we have several adapters, each with several procedures. Our particular backends require some common logic to set up requests and interpret responses. Seems ideal for refactoring common code to shared library, execpt that as far as I can see there's no "library" concept in the adapter environment unless we want to drop down into Java.
Are there any patterns for code-reuse between adapters?

I think you are right. There is currently no way of importing custom JavaScript libraries.
There is a way to include/load Javascript files in Mozilla Rhino engine by using the "load(xyz.js)" function, but this will make your Worklight adapter undeployable.
But I've noticed, that this will make your Worklight adapter undeployable. If you deploy a second *.js file within an adapter, you'll get the following error message:
Adapter deployment failed: Procedure 'getStories' is not implemented in the adapter's JavaScript file.
It seems like Worklight Server can only handle one JavaScript file per adapter.

I have shared some common functionality between adapters by implementing the functionality in Java code and including the jar file in the Worklight war file. This came in handy to invoke stored procs via JDBC that can handle multiple out parms and also retrieving PDF content from internal backend services. The jar needs to be in the lib dir of the worklight.war web app that the adapter will be deployed to.
Example of creating a java object in the adapter:
var parm = new org.apache.http.message.BasicNameValuePair("QBS_Reference_ID",refId);

One way to share JavaScript between adapters is to follow a pattern somewhat like this:
CommonAdapter-impl.js:
var commonObject = {
invokeBackend: function(input, options) {
// Do stuff to check/modify input & options
response = WL.Server.invokeHttp(input);
// Do stuff to check/modify response
return response;
}
}
getCommonObject: function() {
return commonObject;
}
NormalAdapter-impl.js:
function getSomeData(dataNumber) {
var input = {
method: 'get',
returnedContentType: 'json',
path: '/restservices/getsomedata',
}
return _getCommonObject().invokeBackend(input);
}
function _getCommonObject() {
var invocationData = {
adapter: 'CommonAdapter',
procedure: 'getCommonObject',
parameters: []
}
return WL.Server.invokeProcedure(invocationData);
}
In this particular case, the common adapter is being used to provide a "wrapper" function around WL.Server.invokeHttp, but it can be used to provide any other utility functions as well.
The reason for this pattern in particular is that it allows the WL.Server.invokeHttp to run in the context of the "calling" adapter, which means the endpoint (hostname, port number, etc.) specified in the calling adapter's .xml file will be used. It does mean that the short _getCommonObject() function has to be repeated in each "normal" adapter, but this is a fairly small piece of boilerplate.
Note that this pattern has been used with Worklight 6.1 - there is no guarantee it will work in future or past versions.

Related

Set parameter to JUnit5 extension

I wonder how to configure an extension by a test.
Scenario:
The test provides a value that should be used inside the extension.
Current Solution:
The value is defined as field inside the test and used by the extension thru scanning all declared fields and pick the correct one (reflection).
Problem:
This solution is very indirect. People using this extension must know about extension internals and declare the correct fields (type and value). Errors can lead the developer into the right direction, but its a bit of pain by try/error.
Is there a way to use e.g. annotation with value to configure a junit5 extension?
You may either provide configuration parameters and access them via the ExtensionContext at runtime or since 5.1 there's a programmatic way to register customized extension instances via #RegisterExtension.
Example copied from the JUnit 5 User-Guide:
#RegisterExtension
static WebServerExtension server = WebServerExtension.builder()
.enableSecurity(false)
.build();
#Test
void getProductList() {
WebClient webClient = new WebClient();
String serverUrl = server.getServerUrl();
// Use WebClient to connect to web server using serverUrl and verify response
assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
}

How to use hapi-swaggered without a running server

I have a working hapi service, complete with hapi-swaggered and hapi-swaggered-ui. This is useful for many cases, but I want to add a build step to my CI which will be able to get the JSON generated by hapi-swaggered (which, if changed, would get compiled that into an .Net assembly that gets stored in a local proget).
I know that if I really wanted to, on my build server, I could start an instance of my server, curl to localhost:3000/swagger, kill the server, and proceed, but that seems a little risky (i.e., what if I have two builds running at the same time?).
Has anyone developed a way to directly call the hapi-swaggered API to get the raw JSON?
Well, that didn't take too much longer, but I think I found one solution. In this case, internals is my server. It does not auto-start if its loaded (required'ed) from another file, and the compose method is exposed to use hapi's Glue.compose to assemble the service. It seems that I can then use the inject method to simulate a call.
'use strict';
var internals = require('./');
internals.compose(function(err, server) {
server.inject({ method: 'GET', url: '/swagger' }, function (response) {
console.log(JSON.stringify(response.result));
process.exit();
});
});
If there's anything that I'm missing about this technique, I'd like to hear about it.

AngularAMD: the app depends on services but services depend on the app

I am clearly missing something very basic.
The instructions are to create an app, like this:
define(['angularAMD'], function (angularAMD) {
var app = angular.module(app_name, ['webapp']);
... // Setup app here. E.g.: run .config with $routeProvider
return angularAMD.bootstrap(app);
});
And then create subsequent items like this:
define(['app'], function (app) {
app.factory('Pictures', function (...) {
...
});
});
And there is this helpful line:
Any subsequent module definitions would simply need to require app to create the desired AngularJS services
Well that's just great for subsequent module definitions, but app.config and app.run need lots of prerequisite modules that I am supposed to create -- as would any application beyond the level of a toy. So there is obviously some simple solution that I am missing. How do I create services that the app depends on?
You can simply use 'angularAMD' injection to create services.
For example,
define(['angularAMD'], function (angularAMD) {
angularAMD.service('LoggerService',['$log',function($log){
return function(msg){
$log.log('message:', msg);
}
}]);
});
The services created using this method are available before the application is bootstrapped. Hence app can depend on these services.
More similar code can be found at angular-AMD sample app.

intern test not using configured registry requestProvider in dojo

I'm following this article to mock response in dojo.
My mocker is very similar to the one in the article except this:
registry.register(/\/testIntern/, function (url, options) {
return when({
value: "Hello World"
});
In my understanding, this should map to any request that contains "/testIntern" on the address.
My testcase is quite simple:
// similar to example
var testRest= new Rest("/testIntern", true);
testRest("").then(lang.hitch(this, function (data) {
assert.deepEqual("Hello World", data.value, "Expected 'Hello World', but got" + data.value);
}));
It really should be quite simple. But when I run this test, I got 404 Not Found. It looks like the REST call in the test doesn't try to use the mocking service. Why?
You are generally correct in your thought that registering a URL with dojo/request/registry should pass anything referencing that URL via dojo/request through your handler.
Unfortunately, dojo/store/JsonRest uses the dojo/_base/xhr module which uses dojo/request/xhr directly, not dojo/request. Any registrations created with dojo/request/registry (and any setting of defaultProvider) will unfortunately be lost on JsonRest.
You might want to have a look at dstore - its Rest store implements the same server requests as dojo/store/JsonRest but it uses dojo/request instead of being hard-coded to a specific provider. (dojo/request defaults to dojo/request/xhr in browsers anyway, but can be overridden via dojoConfig.requestProvider.) dstore contains adapters for translating between dstore's API and the dojo/store API, if you need to use it with widgets that operate with the latter.

Mocking out AJAX calls with Dojo XHR

I'm attempting to mock the response of a dojo xhr request, but I haven't found a good solution.
Ideally, I'd like to see a solution similar to the jQuery mockjax plugin where I can set a specific call based on a url, e.g.:
$.mockjax({
url: '/restful/fortune',
responseTime: 750,
responseText: {
status: 'success',
fortune: 'Are you a turtle?'
}
});
My initial thought was to utilize the "/dojo/io/send" channel, but I haven't been able to get a modified response to be loaded after modifying the dojo Deferred object.
The other thought is to use a pass-through method that would determine if an actual xhr request should be made, e.g.:
function xhrRequest(xhrArgs) {
if(shouldMock) {
var fakeReturnJson = dojo.toJson({
howdy: "that's odd!",
isStrange: false
});
return fakeReturnJson;
} else {
dojo.xhr(xhrArgs);
}
}
Can someone tell me the best way to go about mocking dojo xhr calls?
Thanks!
It's an old question, but I think you should do your mocking using Sinon.js
However you will need to put the following:
has: { native-xhr2: false }
into your dojoConfig for it to work in 1.8
I haven't heard of any Dojo specific libraries similar to Mockjax. But what I think you could try is use Mockjax with Dojo. This should be pretty easy to do since all you'll have to do is use JQuery during development only for testing with Mockjax and then remove it once development is complete.
I use your second suggestion. Currently, I have a transport layer (simple js class) and 2 implementations (XhrTransport and MockTransport). I then switch in which I need without changing the widget code.
Widgets call the server with:
Controller.send(aServerCall);
where aServerCall is a simple value object with the server endpoint, params and callback.
This way, you can add nice things to the controller that will apply to all server calls (such as logging, analytics, generic error handling...) and also mock out the entire server when doing unit tests.
For the MockTransport, I simply return canned json data from static .js files in the format that the widget expects.