How to call Hapi Plugins for certain routes? - plugins

Im using Hapi 12.1.
Trying to figure out how to call certain extension points only on certain routes.
For example for :
'/hello'
I want to call three different extension points that work on the 'onRequest' step.
For:
'/goodbye'
I want to call a different extension point that works also on the 'onRequest' but is a different operation and a 'onPreAuth' step.
For:
'/health'
Dont call any extension points, and just drop into the handler straight away..
I have tried various ways to create a plugin, define the routes, and extension points. But it seems the the extension points are global, and dont only operation on the plugin's scoped routes.
What am I missing?

You have access to the path on your extension points, using request.route.path. With that, you can define what you want to run, depending on the path. Example:
server.ext('onPreAuth', function (request, reply) {
switch(request.route.path) {
case '/test1':
case '/test2':
// Do something
break;
case '/test3':
// Do something else
break;
}
reply.continue();
});
Alternatively, you can also make it dependent on the route configuration:
server.ext('onPreAuth', function (request, reply) {
if(request.route.settings.plugins.runPreAuth) {
// Do something
}
reply.continue();
});
Then, you just define in your route the configurations:
server.route({
method: 'get',
path: '/test1',
handler: function(request, reply) {
reply({result: 'ok'});
},
config: {
plugins: {
runPreAuth: true
}
}
});

Related

Kuzzle - inter-plugins communication (via method calls)

Is there a way with Kuzzle, to make two plugins communicate with each other?
Let's say a plugin A wants to call a method of a plugin B at boot time, or even runtime for some use cases. How can I do that ?
For now, there is no way to retrieve a particular plugin instance from another plugin. Plugins manager isn't reachable at plugin initialization, but in some way via a Kuzzle request (not the proper way of doing it)
function (request) {
const kSymbol = Object.getOwnPropertySymbols(request.context.user)[0];
request.context.user[kSymbol].pluginsManager.MyPlugin.someMethod(...args);
...
}
The idea behind this question would be to do something like this, when initializing the plugin
function init (customConfig, context) {
const { pluginsManager } = context;
const result = pluginsManager.MyPlugin.someMethod(...args);
// make something with the result ?
// For later use of plugins manager perhaps ?
this.context = context
}
Looks like Kuzzle Pipes would be the right thing to do it, cause they are synchronous and chainable, but pipes don't return anything when triggering an event
function init (customConfig, context) {
const result = context.accessors.trigger('someEvent', {
someAttribute: 'someValue'
});
console.log(result) // undefined
}
Thanks for your help !
full disclosure: I work for Kuzzle
Even if accessing the pluginManager like this may work at this time, we may change the internal implementation without warning since it's not documented so your plugin may not work in next version of Kuzzle.
The easiest way to use a feature from another plugin is exposing a new API method through a custom controller and then call it with the query method of the embedded SDK.
For example if your plugin name is notifier-plugin in the manifest:
this.controllers = {
email: {
send: request => { /* send email */ }
}
};
Then you can call it in another plugin like this:
await this.context.accessors.sdk.query({
controller: 'notifier-plugin/email',
action: 'send'
});
Please note that you can't make API calls in the plugin init method.
If you need to make API calls when Kuzzle start, then you can add a hook/pipe on the core:kuzzleStart event.
this.hooks = {
'core:kuzzleStart': () => this.context.accessors.sdk.query({
controller: 'notifier-plugin/email',
action: 'send'
});
};
Finally I noticed that you can't use pipes returns like in your example but I have proposed a feature to allow plugin developers to use the pipe chain return.
It will be available in the next version of Kuzzle.

Get semantic object and semantic action in controller

I'm trying to get the semantic object and semantic action of my deployed SAPUI5 application. I tried looking into ushell services - URLParsing and LaunchPage but it does not seem to return my semantic objects and actions.
Have anybody tried this?
This worked for me so far:
sap.ui.require([ // modules lazily instead of accessing them with global names.
"sap/ushell/library", // Consider adding `"sap.ushell": { lazy: true }` to dependencies in manifest.json
"sap/ui/core/routing/HashChanger",
], async (sapUshellLib, HashChanger) => {
const fullHash = new HashChanger().getHash(); // returns e.g. "masterDetail-display?sap-ui-theme=sap_fiori_3&/product/HT-1000"
const urlParsing = await sapUshellLib.Container.getServiceAsync("URLParsing");
urlParsing.parseShellHash(fullHash); /** returns e.g. {
action: "display",
appSpecificRoute: "&/product/HT-1000",
contextRaw: undefined,
params: { "sap-ui-theme": "sap_fiori_3" },
semanticObject: "masterDetail"
} **/
});
You can always just use
window.location.hash
Which you can parse yourself pretty easily. If you really want launchpad code, you can often find it here:
sap.ushell.services.AppConfiguration.getCurrentApplication().sShellHash
I've noticed it's not always set though when you're looking at an embedded component
A simplistic way to do this would be:
var oHashObject = new sap.ui.core.routing.HashChanger();
oHashObject.getHash();
//this will return the semantic object and action alongwith the routing params

this$store function is undefined

I am trying to use vuex's store to make some API calls but after installing vuex, importing store to my files and following other stack overflow answers, like making sure vuex is installed, if i am exporting my store file with " Vuex.Store" and etc but my loadCalls function is still not working.
This is the error i get:
this.$store.loadCalls is not a function
Here is my function and how i am trying to call it, it is declared in my ACTIONS section of my store.js file.
loadCalls() {
axios
.get("/some/calls")
.then(res => {
console.log(res)
});
},
I try using it in my beforeMount() when my component loads:
beforeMount(){
this.$store.loadCalls();
}
What am i doing wrong here?
If you defined an action like this:
actions: {
loadCalls() {
// ...
}
}
Then you would call it like this:
this.$store.dispatch('loadCalls');
Actions aren't exposed directly, you call them using dispatch.
https://vuex.vuejs.org/guide/actions.html#dispatching-actions

Writing nested path parameters in FeathersJS

How to generate multi level path parameters in feathers js like below :
api.com/category/catergoryId/subCatergory/subCatergoryId
The following is taken from this Feathers FAQ entry:
Normally we find that they actually aren't needed and that it is much better to keep your routes as flat as possible. For example something like users/:userId/posts is - although nice to read for humans - actually not as easy to parse and process as the equivalent /posts?userId=<userid> that is already supported by Feathers out of the box. Additionaly, this will also work much better when using Feathers through websocket connections which do not have a concept of routes at all.
However, nested routes for services can still be created by registering an existing service on the nested route and mapping the route parameter to a query parameter like this:
app.use('/posts', postService);
app.use('/users', userService);
// re-export the posts service on the /users/:userId/posts route
app.use('/users/:userId/posts', app.service('posts'));
// A hook that updates `data` with the route parameter
function mapUserIdToData(hook) {
if(hook.data && hook.params.userId) {
hook.data.userId = hook.params.userId;
}
}
// For the new route, map the `:userId` route parameter to the query in a hook
app.service('users/:userId/posts').hooks({
before: {
find(hook) {
hook.params.query.userId = hook.params.userId;
},
create: mapUserIdToData,
update: mapUserIdToData,
patch: mapUserIdToData
}
})
Now going to /users/123/posts will call postService.find({ query: { userId: 123 } }) and return all posts for that user.

Ext.define() order

I'm using Extjs5 and Sencha Cmd, and I'm working on a l10n engine (over gettext) to implement localization.
Suppose I want to offer a translation function to every class of my project, named _().
In every controller, view, model and any class, I'd like to be able to write something like that:
Ext.define('FooClass', {
someStrings: [
_('One string to translate'),
_('A second string to translate'),
_('Yet another string to translate')
]
});
First problem: _() must exist before all the Ext.define() of my project are executed. How to achieve that?
Second problem: _() is looking in "catalogs" that are some JavaScript files generated from .po files (gettext). So, those catalogs must have been loaded, before all the Ext.define() of my app are executed.
_() is a synchronous function, it musts immediately return the translated string.
Edit concerning the edited question
You have at least two ways to load External libraries:
Ext.Loader.loadScript
loadScript( options )
Loads the specified script URL and calls the supplied callbacks. If
this method is called before Ext.isReady, the script's load will delay
the transition to ready. This can be used to load arbitrary scripts
that may contain further Ext.require calls.
Parameters
options : Object/String/String[] //The options object or simply the URL(s) to load.
// options params:
url : String //The URL from which to load the script.
onLoad : Function (optional) //The callback to call on successful load.
onError : Function (optional) //The callback to call on failure to load.
scope : Object (optional) //The scope (this) for the supplied callbacks.
If you still run into problems you can force the loader to do a sync loading:
syncLoadScripts: function(options) {
var Loader = Ext.Loader,
syncwas = Loader.syncModeEnabled;
Loader.syncModeEnabled = true;
Loader.loadScripts(options);
Loader.syncModeEnabled = syncwas;
}
Place this in a file right after the ExtJS library and before the generated app.js.
Old Answer
You need to require a class when it is needed, that should solve your problems. If you don't require sencha command/the ExtJS class system cannot know that you need a specific class.
Ext.define('Class1', {
requires: ['Class2'],
items: [
{
xtype: 'combo',
fieldLabel: Class2.method('This is a field label')
}
]
});
For further reading take a look at:
requires
requires : String[]
List of classes that have to be loaded before instantiating this
class. For example:
Ext.define('Mother', {
requires: ['Child'],
giveBirth: function() {
// we can be sure that child class is available.
return new Child();
}
});
uses
uses : String[]
List of optional classes to load together with this class. These
aren't neccessarily loaded before this class is created, but are
guaranteed to be available before Ext.onReady listeners are invoked.
For example:
Ext.define('Mother', {
uses: ['Child'],
giveBirth: function() {
// This code might, or might not work:
// return new Child();
// Instead use Ext.create() to load the class at the spot if not loaded already:
return Ext.create('Child');
}
});
Define the translate function outside the scope of the ExtJs project and include it before the Ext application is included in the index.html.
The scripts are loaded in the right order and the _() function is ready to use in your whole project.
i18n.js
function _() {
// do the translation
}
index.html
<html>
<head>
<script src="i18n.js"></script>
<script id="microloader" type="text/javascript" src="bootstrap.js"></script>
</head>
<body>
</body>
</html>