-
Notifications
You must be signed in to change notification settings - Fork 4
Plugin RegisteredEvent
Implement this abstract method in your plugin class to be able to specify when your plugin should run, what columns are required, and based on the context, what method of the plugin should be executed.
The RegisteredEventBuilder class is used to define RegisteredEvent(s) the plugin will execute for. Here is an example usage:
protected override IEnumerable<RegisteredEvent> CreateEvents()
{
return new RegisteredEventBuilder(PipelineStage.PreOperation, MessageType.Create, MessageType.Update)
.ForEntities<Contact>()
.WithExecuteAction(OnUserEmailRouterAccessApproved)
.WithValidator(new RequirementValidator()
.Updated(new SystemUser { EmailRouterAccessApproval = SystemUser_EmailRouterAccessApproval.Approved }))
.WithAssertValidator(new RequirementValidator()
.Contains<Contact>(ContextEntity.CoalesceTargetPreImage, c => new { c.FullName, c.EmailAddress1 }),
"Name and Email address are required to notify admin of email router access approval!")
.Build();
}
and here is a line-by-line breakdown of what it means:
RegisteredEventBuilder Constructor - This will limit the plugin to run only if the plugin execution context is for the PreOperation
stage of a Create
or Update
message. Any other plugin event will cause the DLaBGenericPluginBase
to throw an exception:
new RegisteredEventBuilder(PipelineStage.PreOperation, MessageType.Create, MessageType.Update)
ForEntities - This will limit the plugin to run only run if the registered table is Contact
. Any other execution of the plugin for a different table will cause the DLaBGenericPluginBase
to throw an exception. This is optional, and if no table/entity is defined, the plugin can be run with any table type:
.ForEntities<Contact>()
WithExecuteAction - Rather than executing the default ExecuteInternal
method of the plugin, the OnUserEmailRouterAccessApproved
method will be called instead. This is optional, and if no execute action is defined, the ExecuteInternal
method of the plugin will be called:
.WithExecuteAction(OnUserEmailRouterAccessApproved)
This allows for multiple RegisteredEvent
's to be registered with different entry points into the plugin, for example having a OnCreate
method for creates and OnUpdate
method for updates:
return new RegisteredEventBuilder(PipelineStage.PreOperation, MessageType.Create)
.WithExecuteAction(OnCreate)
.And(PipelineStage.PreOperation, MessageType.Update)
.WithExecuteAction(OnUpdate);
WithValidator - Defines the validation to be run to determine if the plugin execution should be skipped without error. This is optional, and if no validator is defined, the execute action will always be called. In this example, if the EmailRouterAccessApproval
has not been updated to Approved
then the plugin execution will be skipped without error:
.WithValidator(new RequirementValidator()
.Updated(new SystemUser { EmailRouterAccessApproval = SystemUser_EmailRouterAccessApproval.Approved }))
Note
Only one RequirementValidator
can be defined per RegisteredEvent
.
See the RequirementValidator section for more information on defining a validator.
WithAssertValidator - Defines a requirement that if not true, will cause an exception to be thrown. This is optional, and if no assert validator is defined, no exception will be thrown. Assert validators will only be evaluated after the standard validator (if present) has passed validation, In this example, if the Target (coalesced with the pre-image) does not contain a non-null value for fullname
and emailaddress
an InvalidPluginExecutionException
will be thrown with the message "Name and Email address are required to notify admin of email router access approval!":
.WithAssertValidator(new RequirementValidator()
.Contains<Contact>(ContextEntity.CoalesceTargetPreImage, c => new { c.FullName, c.EmailAddress1 }),
"Name and Email address are required to notify admin of email router access approval!")
The RequirementValidator
is used to determine if a the plugin execution context meets the requirements for the registered event. This can be used with the RegisteredEventBuilder.WithValidator
to control if the plugin execution should be skipped entirely, or the RegisteredEventBuilder.WithAssertValidator
to throw an exception if the context fails validation.
The requirement validator has a combination of fluent methods made up of 8 verbs (Contains, ContainsNullable, ContainsNull, Missing, MissingOrNull, Updated, Changed, Cleared), as well as an optional postfix "Any".
The 8 Verbs:
- Contains - Determines if the entity contains all the defined attributes with a (non-null) value, or if the
Entity
overload is used, the specified values. - ContainsNullable - Same as Contains, but allows null values.
- ContainsNull - Same as Contains, but values must be null.
- Missing - Requires all columns are not present in the entity's Attributes collection.
- MissingOrNull - Requires all columns are not present in the entity's Attributes collection, or if they are present, the are null.
- Not - Requires exact list of specific values to not be present.
- Updated - Determines if the target contains the attribute with a (non-null) value, and the value is different than the value in the pre-image or, if the target contains a particular value for the attribute.
- Changed - Same as Updated, but allows null values.
- Cleared - Same as Updated, but values must be null.
Important
Since setting a string value to the empty string (or white spaces) will result in the column being updated to null in the database, columns that have empty string values will be treated as null.
Post Fix Any:
- Any - Only one of the columns must match the requirements. For example
ContainsAny<Contact>(ContextEntity.Target, c => new { c.EMailAddress1, c.EMailAddress2 })
will be considered valid if the target has a non-null value for eitherEMailAddress1
orEMailAddress2
.
Warning
PreImages do not return an attribute in the collection if the attribute is null. Therefore, any requirement for the PreImage or the CoalesceTargetPreImage being null, nullable, or have a specific value which happens to be null, will be considered valid if the column is not in the Entity.Attributes
collection. This should not be an issue if the PreImage includes the null or nullable column, but if the PreImage registration does not include the column, then this will result in false validations.
Requirement Validator Example The unit testing for the Requirement Validator does a good job of describing the expected results of different calls using an entity with two columns, A and B, in various states (missing from the attributes collection or in the collection but null or empty or populated)
private static readonly Dictionary<string, Expected> IsValidByFunction = new () {
// <******** A Null *********> <******** A Empty ********> <********** A ***********> <********* No A **********>
// ANull AnBn AnBe ANullB AEmpty AeBn AeBe AeB A ABNull ABe AB None BNull BEmpty B
{ nameof(RequirementValidator.Changed), new Expected(false, true, true, true, /**/ false, true, true, true, /**/ false, true, true, true, /**/ false, false, false, false) },
{ nameof(RequirementValidator.ChangedAny), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ true, true, true, true, /**/ false, true, true, true ) },
{ nameof(RequirementValidator.Cleared), new Expected(false, true, true, false, /**/ false, true, true, false, /**/ false, false, false, false, /**/ false, false, false, false) },
{ nameof(RequirementValidator.ClearedAny), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ false, true, true, false, /**/ false, true, true, false) },
{ nameof(RequirementValidator.Contains), new Expected(false, false, false, false, /**/ false, false, false, false, /**/ false, false, false, true, /**/ false, false, false, false) },
{ nameof(RequirementValidator.ContainsAny), new Expected(false, false, false, true, /**/ false, false, false, true, /**/ true, true, true, true, /**/ false, false, false, true ) },
{ nameof(RequirementValidator.ContainsAnyNull), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ false, true, true, false, /**/ false, true, true, false) },
{ nameof(RequirementValidator.ContainsAnyNullable), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ true, true, true, true, /**/ false, true, true, true ) },
{ nameof(RequirementValidator.ContainsNull), new Expected(false, true, true, false, /**/ false, true, true, false, /**/ false, false, false, false, /**/ false, false, false, false) },
{ nameof(RequirementValidator.ContainsNullable), new Expected(false, true, true, true, /**/ false, true, true, true, /**/ false, true, true, true, /**/ false, false, false, false) },
{ nameof(RequirementValidator.Missing), new Expected(false, false, false, false, /**/ false, false, false, false, /**/ false, false, false, false, /**/ true, false, false, false) },
{ nameof(RequirementValidator.MissingAny), new Expected(true, false, false, false, /**/ true, false, false, false, /**/ true, false, false, false, /**/ true, true, true, true ) },
{ nameof(RequirementValidator.MissingOrNull), new Expected(true, true, true, false, /**/ true, true, true, false, /**/ false, false, false, false, /**/ true, true, true, false) },
{ nameof(RequirementValidator.MissingOrNullAny), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ true, true, true, false, /**/ true, true, true, true ) },
{ nameof(RequirementValidator.Not), new Expected(true, true, true, true, /**/ true, true, true, true, /**/ true, true, true, false, /**/ true, true, true, true ) },
{ nameof(RequirementValidator.NotAny), new Expected(true, true, true, false, /**/ true, true, true, false, /**/ false, false, false, false, /**/ true, true, true, false) },
{ nameof(RequirementValidator.Updated), new Expected(false, false, false, false, /**/ false, false, false, false, /**/ false, false, false, true, /**/ false, false, false, false) },
{ nameof(RequirementValidator.UpdatedAny), new Expected(false, false, false, true, /**/ false, false, false, true, /**/ true, true, true, true, /**/ false, false, false, true ) },
};