[Coldbox 5.6.2] How Can I Manually Invoke a Global onInvalidEvent Interceptor?

I’m trying to manually trigger the global onInvalidEvent interceptor in my handlers if a particular entity can’t be found. I created a private method in the handler called getOrFail() which announces the interception point. However it does not seem to be working. I suspect I need to re-create the exact conditions that Coldbox uses when it detects an invalid event and overrides it.

I posted this question on StackOverflow will full code examples in case anyone wants to get some internet points:
https://stackoverflow.com/questions/63082433/how-can-i-manually-invoke-a-global-oninvalidevent-interceptor

Dave

Dave,

You can announce that interceptor at anytime you want, as long as you pass in the right exception object within it.

// Announce it
var iData = {
“invalidEvent” : arguments.event,
“ehBean” : arguments.ehBean,
“override” : false
};
variables.interceptorService.announce( “onInvalidEvent”, iData );

Luis, you’re a rock star, my friend! I saw references to ehBean when investigating how the global onInvalidEvent interceptor worked but did not realize it was passed to all handlers as an argument. this is very helpful! Thank you.

As a side note, you’ll be happy to know I am BDD testing like a total maniac now thanks to you. I hope you and your family are staying safe and doing well.

Dave

Thanks Dave!! ROCK THE BDD!!

That should be a t-shirt!

Yes, it definitely should be a shirt in the Ortus store :slight_smile:

By the way, I ran a test in one of my handlers based on your suggestion, and it looks like ehBean is not passed to handler methods. Is there another way to get the ehBean value from within a handler?
In other words, this handler method will throw a “key [EHBEAN] doesn’t exist in argument scope” error

function show( event, rc, prc ) {

// Announce it
var iData = {
“invalidEvent” : arguments.event,
“ehBean” : arguments.ehBean, // I don’t seem to exist in the handler. :frowning:
“override” : false
};

announceInterception( “onMissingPage”, iData );

}

I should probably give you a little context as to what I’m trying to accomplish with all this. Perhaps I’ve been barking up the wrong tree here.

I’d like to make my handlers a little more DRY, meaning that many of my handler methods require getting an entity object from a service before doing something with it (showing it, deleting it, updating it, etc.). If the entity does not exist, I’d like to show a friendly 404 page. I assumed the best way to accomplish this was to create a new private method in my handler called “getOrFail()” which would attempt to get the entity, and if it couldn’t be found, would halt processing and show the 404 page.

Here’s what a simplified version might look like:

/**

  • Show
    */
    function show( event, rc, prc ) {

param name=“rc.id” default=“0”;
prc.post = getOrFail( rc.id ); // return the entity or fail to a 404 page.

//… show the post
}

/**

  • Delete
    */
    function delete( event, rc, prc ) {

param name=“rc.id” default=“0”;
prc.post = getOrFail( rc.id ); // return the entity or fail to a 404 page.

//… delete the post
}

/**

  • Get Or Fail
  • Gets an entity or redirects to the 404 missing template page
    */
    private function getOrFail( required id ) {

var entity = postService.getById( arguments.id );

if ( !entity.isLoaded() ) {
// announce the interception and override the request
announceInterception( ‘onInvalidEvent’ );
}

return entity;

}

The problem here is that the interceptor doesn’t override the event in the same way Coldbox does it when the handler is missing a handler or handler action. I’m open to any suggestions or tips on how I can handle this type of handler processing. Being able to getOrFail() in a shared method would make things much cleaner.

Prehandler method may help you out as far as checking the rc.id prior to handler method being called.

Thanks for the suggestion, Jamie. Your idea is an interesting one because it means using the Pre Advices as a sort of firewall, which I believe is how cbSecurity works.

If I used preHandler() as you suggest, I would need to give more logic to the preHandler() to make sure it is smart enough to know when rc.id is required, and when it is not.
For example, let’s say I had a typical handler like this:

/**

  • Show
    */

function show( event, rc, prc ) {

param name=“rc.id” default=“0”;
//… show the post
}

/**

  • Delete
    */

function delete( event, rc, prc ) {

param name=“rc.id” default=“0”;
//… delete the post
}

/**

  • list
    */

function list( event, rc, prc ) {

//… list all posts
}

If I added a preHandler() method to look for missing ID’s that preHandler would need to know whether the action was a “show”, “delete”, or “list” because list() doesn’t need rc.id.
In other words, it would need to look something like this:

/**

  • preHandler
    */

function preHandler( event, rc, prc ) {

if (
rc.keyExists( “id” ) && // is there an id in the RC scope?
!postService.doesExistById( rc.id ) // does the record not exist?
{
announceInterception( “onInvalidEvent**” );**
}
}

– interceptor:

/**

  • onInvalidEvent
    */

function onInvalidEvent ( event, interceptData ) {

event.overrideEvent( “error.onMissingPage” );
}

The above should work, but it feels like a code smell to require the PreHandler to know whether rc.id is needed or not. I’ve always looked at preHandler() as something that should apply to all actions in a given handler. Once we start making exceptions to the rules for a particular action, it could cause preHandler() to get complicated rather quickly. That’s just my opinion though as your suggestion of solving the problem seems to work.

Another way I am looking at solving the issue is to not use interceptors at all. Instead, I’m using onError() functionality which does let you hijack the current event and override it with something else. It would be great if interceptors would let you do the same thing, but I think they behave differently at their core.

Here’s an example of how I am accomplishing what I want with the onError() method:

/**

  • Show
    */

function show( event, rc, prc ) {

param name=“rc.id” default=“0”;
prc.post = getOrFail( rc.id ); // return the entity or fail to a 404 page.

//… show the post
}

/**

  • Get Or Fail
  • Gets an entity or redirects to the 404 missing template page
    */
    private function getOrFail( required id ) {

var entity = postService.getById( arguments.id );

if ( !entity.isLoaded() ) {
// throw an error which will be caught by onError()
throw( “entity not found”, “EntityNotFound” );
}

return entity;

}

/**

  • OnError
  • I exist in a base handler!
    */
    function onError( event, rc, prc, faultaction, exception ) {

// log exception
log.error( “The action: #arguments.faultaction# failed when requesting resource: #arguments.event.getCurrentRoutedURL()#” );

switch ( exception.type ) {

// hijack the current event and run a friendly 404 event.
case “EntityNotFound”:
runEvent(
event = “error.onMissingPage”
);
break;

// if this is an actual exception error, blow up as normal.
default:
throw( exception );
break;

}

}

It sounds like pre{action}() may be what you’re after then.
https://coldbox.ortusbooks.com/the-basics/event-handlers/interception-methods

Yes, preAction() would certainly work. However, my initial goal was to try and write more DRY code so I don’t have to repeat myself as much. Writing the same functionality in each preAction() seems like a lot of redundant code, not to mention the additional persistence layer requests.

My initial idea was to see if I could create a private method like this in my handlers and then make a simple method call whenever needed.

/**

  • Get Or Fail
  • Gets an entity or redirects to the 404 missing template page
    */
    private function getOrFail( required id ) {

var entity = postService.getById( arguments.id );

if ( !entity.isLoaded() ) {
// announce the interception and override the request
announceInterception( ‘onInvalidEvent’ );
}

return entity;

}

/**

  • Show
    */
    private function show( required id ) {

var post= postService.getOrFail( arguments.id );
// show it

}

/**

  • Delete
    */
    private function delete( required id ) {

var post= postService.getOrFail( arguments.id );
// delete it

}

Otherwise, if I go the preAction() route, I would have to do this (less DRY):

/**

  • preShow()
    */
    private function preShow( event, rc, prc ) {

var entity = postService.getById( arguments.id );

if ( !entity.isLoaded() ) {
// announce the interception and override the request
announceInterception( ‘onInvalidEvent’ );
}

return entity;

}

/**

  • preDelete()
    */
    private function preDelete( event, rc, prc ) {

var entity = postService.getById( arguments.id );

if ( !entity.isLoaded() ) {
// announce the interception and override the request
announceInterception( ‘onInvalidEvent’ );
}

return entity;

}

/**

  • Show
    */
    private function show( required id ) {

var post= postService.getById( arguments.id );
// show it

}

/**

  • Delete
    */
    private function delete( required id ) {

var post= postService.getById( arguments.id );
// delete it

}

I assumed you would call your private handler method from each of the preAction() calls.

Otherwise, why not use this.prehandler_except to avoid calling the prehandler on events that don’t use rc.id?

Both very interesting ideas, thank you. I had not looked into the _except option as I was not familiar with it.
I also hadn’t considered calling the private method from within the preAction() calls which would allow me to override the event normally. The side effect would be that I have to create a lot of methods in each handler, but this would allow me to accomplish my initial goal.

Thank you for the ideas, I will spend some time experimenting with them.