I have the following javascript file MyService.js:
function(config) {
config.MyService = function(request) {
return call('classpath:path/to/my.feature#tag', request);
};
return config;
}
I load this js from my karate-config.js in order to reuse it from my feature files.
config = karate.callSingle('classpath:path/to/MyService.js', config);
It works as expected and I can call my.feature from any feature file. For example:
Given def res = call MyService myRequest
The problem appears when I try to add a new level to MyService.js function:
function(config) {
config.ApiOauthService = {
myCall : function(request) {
return call('classpath:path/to/my.feature#tag', request);
}
};
return config;
}
When I add the following code to my feature file:
Given def myCall = call MyService.myCall myRequest
I get the following error: "not a js function or feature file"
Do anybody know where is the problem? Is it possible to do what I am trying to do?
Thanks
Yes in Karate 0.9.3 onwards we have limited JS function nesting to only the top-level. You can find a full explanation in this comment.
So if you need JS function "name spacing" you can easily achieve this as per the documentation: https://github.com/intuit/karate#multiple-functions-in-one-file
And if you need more complex nesting, switch to using Java code where you can nest functions at any level.
Related
I'm trying to load my custom module base on the condition.
CustomModule.js
define([], function(){
export {
run: function(){ log.debug('run in CustomModule' }
};
}
And here is my user event script
userevent.js
define(['N/record'], function(record){
...
var moduleName = record.getValue('custom_module_name'); // will return CustomModule.js
require([moduleName], function(customModule){
customModule.run();
});
});
But I got following error
{
type: "error.SuiteScriptModuleLoaderError",
name: "INCORRECT_SUITESCRIPT_CONFIGURATION",
message: "Incorrect SuiteScript configuration for module: CustomModule.js",
}
When I using preload like define(['CustomModule.js'), function(customModule){...})
is working, but this might not suitable for our scenario.
Any suggestion?
So the way you would call a custom module would be (sample snippets below):
CustomModule.js [Code in file cabinet]
define([], function() {
// You can have multiple functions
function testFunction() {
log.debug("Run in CustomModule");
return 123;
}
return { // Return all your functions here
testFunction: testFunction
};
});
And the way you would call the custom module functions in other scripts would be:
Sample User Event Script:
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope SameAccount
*/
define(['./CustomModule.js'], function(customModule) {
.....
function sampleFunction(customerId, recId, invNumberCounter) {
var storeNumber = customModule.testFunction(); // value of 'storeNumber' will be 123
}
.....
});
Additional Important Note:
When writing library scripts (custom modules), have the client-side supported modules in a different script and server-side supported modules in a different script.
This is because, if you incorporate a library script that has a module which is supported only by server-side scripts (eg. N/task module) into a client script, an error would be thrown saying that the module is not supported in this scrpt.
Hope this helps.
The below function in feature files was working for version 0.9.2. Upgraded to 0.9.3 and this gives error : javascript function call failed: Index: 0.0, Size: 0. Code below:
var cnd = ['test1','test2'];
function set_filter(arg)
{
var i;
var filter = {filterValues:[]};
for(i=0;i<arg.length;i++)
{
filter.filterValues[i] = arg[i];
}
return filter;
}
set_filter(cnd)
Also i was earlier able to push values in a javascript array using below, but this has also stopped working in 0.9.3. Get error:javascript function call failed: TypeError: arr.push is not a function
var arr = [];
arr.push('test1','test2');
Try the scenario below that works in 0.9.2 but reports error (mentioned above) in 0.9.3
Scenario: JS test
* def filter_template =
"""
function() {
var filter_params = {
filterValues:[]
};
return filter_params;
}
"""
* def template = call filter_template
* def filter_condition = ['test1','test2']
* def setFilter =
"""
function(arg) {
var i;
var filter = arg.filter_template;
for(i=0;i<arg.condition.length;i++)
{
filter.filterValues[i] = arg.condition[i];
}
return filter;
}
"""
* def getFilter = call setFilter { filter_template: '#(template)', condition: '#(filter_condition)' }
* print getFilter
Help is much appreciated.
We've made the JS conversions stricter, everything will be a Java collection behind the scenes. If you make this change, things will start working:
filter.filterValues.set(i, arg.condition.get(i));
The same goes for push() - use add() or karate.appendTo(varname, value) instead.
My strong recommendation is don't use too much of JS logic especially loops. Karate has functions such as map(), forEach() and repeat() to solve for these needs. Please refer the docs here: https://github.com/intuit/karate#loops
You will thank me later because it will make your scripts easier to understand and maintain. One reason why this is needed is to pave the way for us to change the JS engine in the future.
Suppose I have saved followings functions in a Utility js file.
function getCurrentDate(){
return 'date';
}
function getMonth(){
return 'Oct';
}
Please help me how any of these methods can be accessed in feature file.
I tried following code but it is not working.
* def fun = call read('Utility.js')
* def result = getData()
or
* def result = fun.getData()
In Karate, a JS file can contain only one function and it does not need a name, take a closer look at the examples.
I don't really recommend combining multiple functions into one file, it just makes things much harder to maintain. But if you really insist, here's how:
function() {
return {
getCurrentDate: function(){ return 'date' },
getMonth: function(){ return 'month' }
}
}
EDIT: a much better answer is here: https://stackoverflow.com/a/49384760/143475
* def data = karate.read('classpath:<path>/getRandomString.js')(<inputToTheFunctionIfAny>);
The above code works.
Is there a nice way to prevent duplicate routes from being registered in express? I have a pretty large application with hundreds of routes across different files, and it gets difficult to know if I've already registered a certain route when I go to add a new one. For example, I'd like to throw an error when express gets to routes487.js:
File: routes1.js
var ctrl = require('../controllers/testctrl');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi);
app.get('/there', auth.getToken, ctrl.there);
};
File: routes487.js
var ctrl = require('../controllers/testctrl487');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi487);
};
You could try a custom solution by wrapping express methods with the validation. Consider the following modification to your express app:
// route-validation.js
module.exports = function (app) {
var existingRoutes = {}
, originalMethods = [];
// Returns true if the route is already registered.
function routeExists(verb, path) {
return existingRoutes[verb] &&
existingRoutes[verb].indexOf(path) > -1;
}
function registerRoute(verb, path) {
if (!existingRoutes[verb]) existingRoutes[verb] = [];
existingRoutes[verb].push(path);
}
// Return a new app method that will check repeated routes.
function validatedMethod(verb) {
return function() {
// If the route exists, app.VERB will throw.
if (routeExists(verb, arguments[0]) {
throw new Error("Can't register duplicate handler for path", arguments[0]);
}
// Otherwise, the route is saved and the original express method is called.
registerRoute(verb, arguments[0]);
originalMethods[verb].apply(app, arguments);
}
}
['get', 'post', 'put', 'delete', 'all'].forEach(function (verb) {
// Save original methods for internal use.
originalMethods[verb] = app[verb];
// Replace by our own route-validator methods.
app[verb] = validatedMethod(verb);
});
};
You just need to pass your app to this function after creation and duplicate route checking will be implemented. Note that you might need other "verbs" (OPTIONS, HEAD).
If you don't want to mess with express' methods (we don't know whether or how express itself or middleware modules will use them), you can use an intermediate layer (i.e., you actually wrap your app object instead of modifying its methods). I actually feel that would be a better solution, but I feel lazy to type it right now :)
I have implemented HTTP adapter in IBM Worklight. I want to display the result returned from server. I want to display HTML file. My code is
function getFeeds() {
var input = {
method : 'get',
returnedContentType : 'text',
path : "marketing/partners.html"
};
WL.Logger.debug("sdfsds");
return WL.Server.invokeHttp(input);
}
I want to receive(display) WL.Server.invokeHttp(input). After receiving it I want to parse the data.
Take a look at the Server-side Development Getting Started Modules. Inside the HTTP adapter – Communicating with HTTP back-end systems Module on Slide 15 - 'XSL Transformation Filtering' will show you how to filter data you get back from the backend. Further parsing and showing data has to be done on the client using onSuccess callback for WL.Client.invokeProcedure. There's a module for that too.
Here's an example of getting data and showing to a user:
var invocationData = {
adapter : 'adapter-name',
procedure : 'procedure-name',
parameters : []
};
var options = {};
options.onSuccess = function (response) {
//response is a JavaScript object
$("#id").html(response.invocationResponse.text);
}
options.onFailure = function (response) {
alert('Failed!'); //You probably want something more meaningful here.
}
WL.Client invokeProcedure(invocationData, options);
There are JavaScript libraries you can add to make searching for values inside the JSON response easier, such as: jspath and jquery-jspath. There's also XPath if you're working with XML.
If you retrieve it as plain text, once you got it back to your application, do something like
$("#container-id").html(response.invocationResponse.text);
This will inject the HTML you've retrieved to an element with id container-id.