Plugin Development: Accessing plugins with 'this'

cms
plugins

#1

Hi everyone,

we had an interesting topic in the Jovo Developer Slack today and I wanted to continue the conversation here in the forum to prevent it from going down in a giant thread.

@Nic wants to create a plugin similar to the GoogleSheetsCMS integration, as some kind of rule engine (please correct me if I’m wrong @Nic :slight_smile: ). His plugin implementation works so far, but he needed some help as to how to access the values like in the sheets integration with this.$cms.t().

Here is the conversation: https://jovodevs.slack.com/archives/C82DKJ7T5/p1554635124048800

So, @Nic, to continue our thread, as I mentioned, you need to dig deeper into the framework for this.
$cms is an attribute registered on both Jovo.ts and BaseApp.ts in /jovo-core. Jovo.ts exports an abstract class Jovo, which is extended by the various platform classes (like AlexaSkill.ts). The $cms object in Jovo is also the object that you call with this.$cms in your skill logic. In the GoogleSheetsCMS plugin you can see the values from parse() (which essentially makes all sheet values accessible) being registered to handleRequest.app.$cms, which means that the values are assigned to the $cms object on BaseApp, which then are copied to the Jovo.$cms object. This happens in BaseCmsPlugin.ts in copyCmsDataToContext().

So, depending on what you want to achieve with your plugin, you could register a new attribute to the Jovo class, for example $cmsRules, assign all the values to this object in your plugin and then try to access it in an example project with this.$cmsRules. I hope that clarifies things a bit.

I’m looking forward to hearing your ideas and opinions!


#2

Thanks a lot for this, @rubenaeg :clap:

Also, for people who are getting started with Jovo Plugin development, here is a great resource by @falicon:

And a tutorial by @Kaan_Kilic:


#3

Hello, thanks for these important information. So yes my final idea is to extend the CMS jovo plug in with a rule engine. The basic idea is to allow minimal changes to the skill itself having the rule engine implementing the business logic through the rules. The rules can be written in the CMS spreadsheet and then converted in json that is the format that the rule engine wants. After that the rule engine gets initialised and the only think the skills has to do is sends facts to the rules CMS. In this way if we use generic enough facts changing the rules in the CMS sheet we could change the business (even complex) logic of the skills without reworking it. This is my final target before this I need to understand better the jovo framework for that I m working on a much simpler plug in ( this is the one almost finished) to get used to the jovo way.


#4

So I have integrated my plugin in my project following (the very useful) video that Jan shared here.

It works everything as expected but I am not able to call a “custom” middleware event!

As said before I am following the Google spreadsheet CMS and I have seen that is possible creating custom middleware event thanks to the ActionSet. I have done so in the Main plugin that (similar to the CMS one ) after the set up call the custom middleware event. This event is never fired in the “Child” plugin…

Because I could not find any documentation I wanted to ask, is my understanding of the ActionSet mechanism correct?
I have printed some logs and seems that

await this.middleware(“configure”)!.run(handleRequest, true);

actually does not do what it says in the docs. In the docs says that calls all the functions in the array of of fns.

In that array there is correctly the function of the “child plugin” registered but it does not get called at all…

I do not know what is missing

Thanks again


#5

Hey @Nic, sorry it took that long to get back to you.

Regarding your problem with the Actionset, there are different middleware handlers that register functions. So, there is a middleware handler for the App object and a middleware handler for your plugin. Could you please give more detail as to how you register your function to the middleware handler and how you call it?


#6

Hey @rubenaeg, thanks for your answer :slight_smile:

So taking as an example the GoogleSheetCMS I have a “master” plugin that uses other “child” plugin depending on the value of a configuration property.

Every plugin then has got a specific configuration that is an extension of the Master plugin.

When the master plugin initialises the specific plugin according to the value passed in the config, it passes also its own config in the child plugin constructor. This is the same that is happening in the function install line 72 of the GoogleSheetCMS.
After this, My master plugin exectures
await this.middleware("configure.drive")!.run(handleRequest, true);
Note that the master plugin class extends Extensible implements Plugin
and also defines
this.actionSet = new ActionSet(["configure.drive"], this);
in the child plugin I have the following in the install function (called when the master plugin is execute the this.use(new childplugin()) )
install(extensible: Extensible) { console.log("child middleware", extensible.middleware("configure")); console.log("child config", this.config); extensible.middleware("configure.drive")!.use(this.init.bind(this)); }

Now in the logs, I can see that in the fns of the middleware there is an array with an Asynchronous function bound ==> init but when the Master plug-in executes await this.middleware("configure.drive")!.run(handleRequest, true);
The init function in the child plugin does not get executed.

I am also not very clear how the merge of the configs works (it does not work for me that part either but I think the middleware call is more important at the moment: stuck_out_tongue:)
Hope this clarifies a bit more my situation. I am also happy to share the code with you even if I would like to complete it before sharing with everybody :stuck_out_tongue:
Best regards

Nic


#7

Correct me if I’m wrong, but it seems like you are calling await this.middleware("configure.drive")!.run(handleRequest, true); before extensible.middleware("configure.drive")!.use(this.init.bind(this));, right?


#8

Uhm Well I would say no per my understanding of how this wokrs but I am sure I do not understand it correctly lol.
So I my master install I do this.use( new ChildPlugin()); so this in theory should call the install of Child where i register the listener for the configure.drive event .

Then in Master Plugin is set up the listener of the app setup middleware and there I call
this.middleware("configure.drive")!.run(handleRequest, true);

So if the Setup of Master plug in runs after the install of Child plugin I think Child plugin should have the listener for configure.drive already set no?


#9

Could you log the middleware right before you call this.middleware("configure.drive")!.run(handleRequest, true); and post the result here? Currently I’m also unsure of what the problem could be.


#10

Hello ,

So this is when the install of the child plugin get s called before it hooks the to the middleware final cinfig { useCredentials: true, bucketName: '', region: 'eu-west-2', credentials: { key: '', secret: '' }, type: 's3' } S3 middleware Middleware { enabled: true, name: 'configure', parent: AwsDrive { domain: null, _events: [Object: null prototype] {}, _eventsCount: 0, _maxListeners: 0, config: { credentials: [Object], type: 's3' }, '$plugins': Map { 'S3Plugin' => [S3Plugin] }, actionSet: ActionSet { middleware: [Map] }, baseApp: App { domain: null, _events: [Object], _eventsCount: 3, _maxListeners: 0, config: [Object], '$plugins': [Map], actionSet: [ActionSet], initialized: false, '$platform': [Map], '$cms': {}, '$data': {}, middlewares: [Array], '$config': [Object], '$db': [FileDb] } }, fns: [] } s3 config { useCredentials: true, bucketName: '', region: 'eu-west-2', credentials: { key: '', secret: '' }, type: 's3' }

As you can see the fns of the middleware is empty
Before calling this.middleware("configure.drive")!.run(handleRequest, true);
I have the following logs : console.log('calling configure'); console.log("Function array middle ware",this.middleware("configure.drive")!.fns )
and these are the output

calling configure

(*) Function array middle ware [ [AsyncFunction: bound initS3] ]
As you can see the Master plugin can see the function bound initS3 but when executes

await this.middleware("configure.drive")!.run(handleRequest, true);

I cannot see the logs that I have in InitS3 function of the child plugin.
the entire lines of codes are:

console.log('calling configure');

console.log("Function array middleware",this.middleware("configure.drive")!.fns ) //i can see initS3 being bound here as shown in (*)

await this.middleware("configure.drive")!.run(handleRequest, true); // I would expect to see the logs presents in the function initS3 but they do not appear
Please let me know if you want more logs
Regards


#11

Weird, do the logs in initS3 have any conditional statement that could avoid them from being logged?