-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathModuleConfig.bx
More file actions
149 lines (138 loc) · 6.33 KB
/
Copy pathModuleConfig.bx
File metadata and controls
149 lines (138 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
* Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp
* www.ortussolutions.com
* ---
*/
class {
// Module Properties
this.title = "rulebox"
this.author = "Ortus Solutions"
this.webURL = "https://www.ortussolutions.com"
this.description = "A natural language rule engine for ColdBox apps"
// Model Namespace
this.modelNamespace = "rulebox"
// Class Mapping
this.classMapping = "rulebox"
// Dependencies
this.dependencies = []
// Global application helper mixins (handlers/views/layouts): ruleBook( name )
this.applicationHelper = [ "mixins/Helpers.bxm" ]
// Enables convention-based routing for the visualizer's handler/views under /rulebox-visualizer.
// The visualizer itself stays inert (routes 404, no events recorded) unless settings.visualizer.enabled = true.
this.entryPoint = "rulebox-visualizer"
/**
* The default `visualizer` settings. A module is loaded once, so they live in a static struct
* that configure() copies and onLoad() uses to fill the keys an app's (shallow-merged)
* override left out. Nothing writes to it.
*/
static {
VISUALIZER_DEFAULTS = {
// Master switch. While false, the visualizer's routes 404 and no rule events are
// recorded or broadcast at all - flipping this on is the only thing that turns on
// the (small) per-rule-evaluation bookkeeping cost.
enabled = false,
// WireBox mapping ID for the metrics persistence store. Defaults to the in-memory
// store (no setup, nothing survives a restart). For persistence across restarts,
// point this at "SQLiteMetricsStore@rulebox" - see the "Rule Visualizer" guide for
// what that needs (the bx-sqlite module plus a matching datasource), or implement
// IMetricsStore@rulebox yourself and point this at its mapping.
metricsStore = "InMemoryMetricsStore@rulebox",
// Datasource name SQLiteMetricsStore reads/writes, if you opt into it above.
datasourceName = "rulebox_visualizer",
// Max live-tracker (SSE) connections open at once; each pins two server threads. Over the cap, stream() answers 503.
maxStreams = 25,
// SQLiteMetricsStore retention (0 disables either): prune events older than this many days...
retentionDays = 30,
// ...and keep at most this many rows. Checked every ~500 inserts, not on every insert.
maxStoredEvents = 100000,
// SQLiteMetricsStore circuit breaker: after this many consecutive failures stop
// writing for the cool-down (seconds), logging one error on open and one info on recovery.
circuitBreakerThreshold = 5,
circuitBreakerCooldownSeconds = 60
}
}
/**
* Configure Module
*/
function configure(){
variables.settings = {
// Named rulebook declarations: { name : path | inline-array | { source, actions, predicates } }
// See the "Externalized Rule Definitions" guide for the full settings shape.
rulebooks = {},
// *.json/*.yaml files dropped here are auto-discovered; name = filename without extension
conventionPath = "config/rulebox",
// The Rule Visualizer: a dashboard/dry-run/metrics/live-tracker admin UI, off by default.
// Secure it yourself (e.g. with cbSecurity) once enabled - RuleBox doesn't gate access on its own.
// See the "Rule Visualizer" guide for the full settings shape.
visualizer = duplicate( static.VISUALIZER_DEFAULTS )
}
}
/**
* Fired when the module is registered and activated.
*/
function onLoad(){
// ColdBox merges an app's moduleSettings over ours with a SHALLOW append, so an app that only
// writes `visualizer = { enabled = true }` replaces our whole `visualizer` struct. Put the
// defaults back underneath whatever the app supplied, then validate. This is the one place
// the visualizer settings are completed and checked: every reader trusts them as-is.
if( !isStruct( variables.settings.visualizer ?: "" ) ){
invalidSetting( "visualizer", variables.settings.visualizer ?: "null", "a struct" )
}
variables.settings.visualizer.append( static.VISUALIZER_DEFAULTS, false )
validateVisualizerSettings( variables.settings.visualizer )
// Custom injection DSL: inject="rulebook" (the registry) / inject="rulebook:{name}" (a provider)
wirebox.registerDSL( namespace = "rulebook", path = "rulebox.models.RuleBookDSL" )
}
/**
* Validate the completed visualizer settings so a bad config fails on app start, not on first use.
*
* @visualizer The visualizer settings, already filled in from VISUALIZER_DEFAULTS
*
* @throws RuleBox.InvalidSettingException When a setting has the wrong type or an out-of-range value
*/
private void function validateVisualizerSettings( required struct visualizer ){
if( isNull( arguments.visualizer.enabled ) || !isBoolean( arguments.visualizer.enabled ) ){
invalidSetting( "visualizer.enabled", arguments.visualizer.enabled ?: "null", "true or false" )
}
for( var key in [ "metricsStore", "datasourceName" ] ){
var value = arguments.visualizer[ key ] ?: ""
if( !isSimpleValue( value ) || !len( trim( value ) ) ){
invalidSetting( "visualizer.#key#", value, "a non-blank WireBox mapping or name" )
}
}
// Whole-number settings and the smallest value each accepts
var minimums = {
retentionDays : 0,
maxStoredEvents : 0,
circuitBreakerThreshold : 1,
circuitBreakerCooldownSeconds : 0,
maxStreams : 1
}
for( var key, minimum in minimums ){
var value = arguments.visualizer[ key ] ?: ""
if( !isNumeric( value ) || value < minimum || value != int( value ) ){
invalidSetting( "visualizer.#key#", value, "a whole number of #minimum# or more" )
}
}
}
/**
* Throw the exception every visualizer setting check uses, naming the key and the bad value.
*
* @key The setting path under moduleSettings.rulebox, such as "visualizer.enabled"
* @value The value the app supplied
* @expected A short description of what the setting accepts
*
* @throws RuleBox.InvalidSettingException Always
*/
private void function invalidSetting( required string key, any value, required string expected ){
throw(
type = "RuleBox.InvalidSettingException",
message = "Invalid moduleSettings.rulebox.#arguments.key#: expected #arguments.expected#, got #( isSimpleValue( arguments.value ?: "" ) ? "'#arguments.value ?: ""#'" : "a complex value" )#."
)
}
/**
* Fired when the module is unregistered and unloaded
*/
function onUnload(){
}
}