@theotherwillembotha/node-red-plugincore 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +616 -220
  2. package/build/GenerateNodes.js +9 -33
  3. package/build/Nodes.html +2 -1505
  4. package/build/Nodes.js +5 -19
  5. package/build/Plugins.html +11 -0
  6. package/build/Plugins.js +17 -12
  7. package/build/core/NodeConstructor.d.ts +12 -16
  8. package/build/core/NodeConstructor.d.ts.map +1 -1
  9. package/build/core/NodeConstructor.js +125 -185
  10. package/build/core/NodeGenerator.d.ts +3 -1
  11. package/build/core/NodeGenerator.d.ts.map +1 -1
  12. package/build/core/NodeGenerator.js +139 -43
  13. package/build/core/NodeManagerRuntime.d.ts +44 -0
  14. package/build/core/NodeManagerRuntime.d.ts.map +1 -0
  15. package/build/core/NodeManagerRuntime.js +140 -0
  16. package/build/core/Utils.d.ts.map +1 -1
  17. package/build/core/logger/service/LoggerService.d.ts +8 -6
  18. package/build/core/logger/service/LoggerService.d.ts.map +1 -1
  19. package/build/core/logger/service/LoggerService.js +101 -23
  20. package/build/core/logger/template/LoggerTemplate.d.ts +1 -2
  21. package/build/core/logger/template/LoggerTemplate.d.ts.map +1 -1
  22. package/build/core/logger/template/LoggerTemplate.js +18 -5
  23. package/build/core/metrics/MetricsDecorator.d.ts +1 -2
  24. package/build/core/metrics/MetricsDecorator.d.ts.map +1 -1
  25. package/build/core/metrics/MetricsDecorator.js +89 -116
  26. package/build/core/metrics/node/CounterMetricConfigNode.d.ts.map +1 -1
  27. package/build/core/metrics/node/CounterMetricConfigNode.js +4 -5
  28. package/build/core/metrics/node/GaugeMetricConfigNode.d.ts.map +1 -1
  29. package/build/core/metrics/node/GaugeMetricConfigNode.js +5 -6
  30. package/build/core/metrics/node/MetricsConfigNode.d.ts +19 -9
  31. package/build/core/metrics/node/MetricsConfigNode.d.ts.map +1 -1
  32. package/build/core/metrics/node/MetricsConfigNode.js +14 -45
  33. package/build/core/metrics/node/TimerMetricConfigNode.d.ts.map +1 -1
  34. package/build/core/metrics/node/TimerMetricConfigNode.js +11 -17
  35. package/build/core/metrics/service/MetricsService.d.ts +110 -114
  36. package/build/core/metrics/service/MetricsService.d.ts.map +1 -1
  37. package/build/core/metrics/service/MetricsService.js +122 -339
  38. package/build/core/metrics/template/CounterMetricTemplate.d.ts +1 -2
  39. package/build/core/metrics/template/CounterMetricTemplate.d.ts.map +1 -1
  40. package/build/core/metrics/template/CounterMetricTemplate.js +16 -5
  41. package/build/core/metrics/template/GaugeMetricTemplate.d.ts +1 -2
  42. package/build/core/metrics/template/GaugeMetricTemplate.d.ts.map +1 -1
  43. package/build/core/metrics/template/GaugeMetricTemplate.js +16 -5
  44. package/build/core/metrics/template/MetricsTemplate.d.ts +1 -2
  45. package/build/core/metrics/template/MetricsTemplate.d.ts.map +1 -1
  46. package/build/core/metrics/template/MetricsTemplate.js +18 -6
  47. package/build/core/metrics/template/TimerMetricTemplate.d.ts +1 -2
  48. package/build/core/metrics/template/TimerMetricTemplate.d.ts.map +1 -1
  49. package/build/core/metrics/template/TimerMetricTemplate.js +16 -5
  50. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts +1 -1
  51. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts.map +1 -1
  52. package/build/core/other/node/DelegatedConfigReferenceNode.js +1 -1
  53. package/build/core/other/service/InputService.d.ts.map +1 -1
  54. package/build/core/other/service/SettingsService.d.ts +1 -2
  55. package/build/core/other/service/SettingsService.d.ts.map +1 -1
  56. package/build/core/other/service/SettingsService.js +19 -5
  57. package/build/core/other/template/BasicTemplate.d.ts +1 -2
  58. package/build/core/other/template/BasicTemplate.d.ts.map +1 -1
  59. package/build/core/other/template/BasicTemplate.js +15 -5
  60. package/build/core/other/template/SettingsTemplate.d.ts +1 -2
  61. package/build/core/other/template/SettingsTemplate.d.ts.map +1 -1
  62. package/build/core/other/template/SettingsTemplate.js +17 -5
  63. package/build/core/state/node/InternalStateConfigNode.d.ts +16 -0
  64. package/build/core/state/node/InternalStateConfigNode.d.ts.map +1 -0
  65. package/build/core/state/node/InternalStateConfigNode.js +89 -0
  66. package/build/core/state/node/StateConfigNode.d.ts +24 -0
  67. package/build/core/state/node/StateConfigNode.d.ts.map +1 -0
  68. package/build/core/state/node/StateConfigNode.js +22 -0
  69. package/build/core/state/service/StateService.d.ts +34 -0
  70. package/build/core/state/service/StateService.d.ts.map +1 -0
  71. package/build/core/state/service/StateService.js +201 -0
  72. package/build/core/state/template/StateTemplate.d.ts +7 -0
  73. package/build/core/state/template/StateTemplate.d.ts.map +1 -0
  74. package/build/core/state/template/StateTemplate.js +25 -0
  75. package/build/core/tagging/ServiceDescriptionDecorator.d.ts +10 -0
  76. package/build/core/tagging/ServiceDescriptionDecorator.d.ts.map +1 -0
  77. package/build/core/tagging/ServiceDescriptionDecorator.js +10 -0
  78. package/build/core/tagging/TemplateDescriptionDecorator.d.ts +8 -0
  79. package/build/core/tagging/TemplateDescriptionDecorator.d.ts.map +1 -0
  80. package/build/core/tagging/TemplateDescriptionDecorator.js +10 -0
  81. package/build/core/tagging/service/NodeTypeService.d.ts +1 -2
  82. package/build/core/tagging/service/NodeTypeService.d.ts.map +1 -1
  83. package/build/core/tagging/service/NodeTypeService.js +19 -5
  84. package/build/core/ui/template/ScriptEditorTemplate.d.ts +1 -2
  85. package/build/core/ui/template/ScriptEditorTemplate.d.ts.map +1 -1
  86. package/build/core/ui/template/ScriptEditorTemplate.js +15 -5
  87. package/build/core/ui/template/UIHelperTemplate.d.ts +1 -2
  88. package/build/core/ui/template/UIHelperTemplate.d.ts.map +1 -1
  89. package/build/core/ui/template/UIHelperTemplate.js +15 -5
  90. package/build/core/webhook/node/WebhookServerConfigNode.d.ts.map +1 -1
  91. package/build/core/webhook/service/ReverseProxyTypeService.d.ts.map +1 -1
  92. package/build/core/webhook/service/WebhookServerService.d.ts +3 -4
  93. package/build/core/webhook/service/WebhookServerService.d.ts.map +1 -1
  94. package/build/core/webhook/service/WebhookServerService.js +48 -18
  95. package/build/core/webhook/template/WebhookTemplate.d.ts +1 -2
  96. package/build/core/webhook/template/WebhookTemplate.d.ts.map +1 -1
  97. package/build/core/webhook/template/WebhookTemplate.js +19 -6
  98. package/build/index.d.ts +6 -2
  99. package/build/index.d.ts.map +1 -1
  100. package/build/index.js +7 -2
  101. package/build/runtime/NodeManagerRuntime.js +140 -0
  102. package/documentation/CounterMetricConfigNode.png +0 -0
  103. package/documentation/GaugeMetricConfigNode.png +0 -0
  104. package/documentation/LoggerTemplate.png +0 -0
  105. package/documentation/MetricsTemplate.png +0 -0
  106. package/documentation/StateTemplate.png +0 -0
  107. package/documentation/TimerMetricConfigNode_histogram.png +0 -0
  108. package/documentation/TimerMetricConfigNode_summary.png +0 -0
  109. package/documentation/WebhookServerConfigNode.png +0 -0
  110. package/documentation/WebhookTemplate.png +0 -0
  111. package/package.json +4 -11
  112. package/src/GenerateNodes.ts +10 -40
  113. package/src/core/NodeConstructor.ts +150 -228
  114. package/src/core/NodeGenerator.ts +156 -47
  115. package/src/core/NodeManagerRuntime.ts +179 -0
  116. package/src/core/logger/service/LoggerService.ts +60 -32
  117. package/src/core/logger/template/LoggerTemplate.html +11 -6
  118. package/src/core/logger/template/LoggerTemplate.ts +9 -11
  119. package/src/core/metrics/MetricsDecorator.ts +140 -168
  120. package/src/core/metrics/node/CounterMetricConfigNode.ts +20 -20
  121. package/src/core/metrics/node/GaugeMetricConfigNode.ts +24 -25
  122. package/src/core/metrics/node/MetricsConfigNode.ts +29 -48
  123. package/src/core/metrics/node/TimerMetricConfigNode.ts +50 -59
  124. package/src/core/metrics/service/MetricsService.ts +214 -489
  125. package/src/core/metrics/template/CounterMetricTemplate.html +0 -1
  126. package/src/core/metrics/template/CounterMetricTemplate.ts +8 -11
  127. package/src/core/metrics/template/GaugeMetricTemplate.html +1 -1
  128. package/src/core/metrics/template/GaugeMetricTemplate.ts +8 -11
  129. package/src/core/metrics/template/MetricsTemplate.html +132 -13
  130. package/src/core/metrics/template/MetricsTemplate.ts +9 -10
  131. package/src/core/metrics/template/TimerMetricTemplate.ts +8 -10
  132. package/src/core/other/node/DelegatedConfigReferenceNode.ts +1 -1
  133. package/src/core/other/service/SettingsService.ts +6 -10
  134. package/src/core/other/template/BasicTemplate.html +1 -1
  135. package/src/core/other/template/BasicTemplate.ts +6 -11
  136. package/src/core/other/template/SettingsTemplate.ts +8 -13
  137. package/src/core/state/node/InternalStateConfigNode.html +77 -0
  138. package/src/core/state/node/InternalStateConfigNode.ts +102 -0
  139. package/src/core/state/node/StateConfigNode.ts +29 -0
  140. package/src/core/state/service/StateService.ts +180 -0
  141. package/src/core/state/template/StateTemplate.html +244 -0
  142. package/src/core/state/template/StateTemplate.ts +18 -0
  143. package/src/core/tagging/ServiceDescriptionDecorator.ts +23 -0
  144. package/src/core/tagging/TemplateDescriptionDecorator.ts +19 -0
  145. package/src/core/tagging/service/NodeTypeService.ts +6 -11
  146. package/src/core/ui/template/ScriptEditorTemplate.ts +6 -10
  147. package/src/core/ui/template/UIHelperTemplate.html +41 -26
  148. package/src/core/ui/template/UIHelperTemplate.ts +6 -10
  149. package/src/core/webhook/service/WebhookServerService.ts +39 -23
  150. package/src/core/webhook/template/WebhookTemplate.html +28 -35
  151. package/src/core/webhook/template/WebhookTemplate.ts +10 -12
  152. package/src/index.ts +8 -2
  153. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts +0 -20
  154. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts.map +0 -1
  155. package/build/core/logger/node/ConsoleLoggerConfigNode.js +0 -62
  156. package/build/core/logger/node/RestLoggerConfigNode.d.ts +0 -40
  157. package/build/core/logger/node/RestLoggerConfigNode.d.ts.map +0 -1
  158. package/build/core/logger/node/RestLoggerConfigNode.js +0 -128
  159. package/documentation/ConsoleLoggerConfigNode.png +0 -0
  160. package/documentation/MetricsConfigNode.png +0 -0
  161. package/documentation/RestLoggerConfigNode.png +0 -0
  162. package/documentation/TimerMetricConfigNode.png +0 -0
  163. package/documentation/WebhookNode.png +0 -0
  164. package/src/core/logger/node/ConsoleLoggerConfigNode.html +0 -90
  165. package/src/core/logger/node/ConsoleLoggerConfigNode.ts +0 -71
  166. package/src/core/logger/node/RestLoggerConfigNode.html +0 -183
  167. package/src/core/logger/node/RestLoggerConfigNode.ts +0 -170
@@ -1,560 +1,285 @@
1
- import {BaseNode, BaseNodeConfig, BaseService, FlowDeployment, ServiceDescriptor} from "../../NodeConstructor"
2
- import client, {Counter, Gauge, Histogram, HistogramConfiguration, MetricObjectWithValues, MetricValueWithName, register as PrometheusRegistry, Registry, Summary, SummaryConfiguration } from "prom-client";
1
+ import {BaseNode, BaseNodeConfig, BaseService, FlowDeployment} from "../../NodeConstructor"
2
+ import { ServiceDescription } from "../../tagging/ServiceDescriptionDecorator";
3
3
  import { NodeAPI, NodeAPISettingsWithData } from "node-red";
4
- import deepEqual from "deep-equal";
5
4
 
5
+ // ******************************************************* //
6
+ // Capability //
7
+ // ******************************************************* //
6
8
 
7
- export abstract class Metric<ConfigType extends MetricConfig> {
8
- private _config: ConfigType;
9
- private _labels: Labels;
10
-
11
- protected constructor(config:ConfigType){
12
- this._config = config;
13
- this._labels = {flow:config.node.flow, type:config.node.type, name:config.node.name, id:config.node.id, metric:config.metricname};
14
- }
15
-
16
- public labels():Labels {
17
- return this._labels;
18
- }
19
-
20
- public config():ConfigType{
21
- return this._config;
22
- }
9
+ export enum MetricCapability {
10
+ Counter = "Counter",
11
+ Gauge = "Gauge",
12
+ Histogram = "Histogram",
13
+ Summary = "Summary",
23
14
  }
24
15
 
25
- export type MetricsReference = {
26
- metricsEnabled:boolean,
27
- metricsReference:string
28
- }
29
-
30
- // **************************************************** //
31
- // Counter //
32
- // **************************************************** //
33
-
16
+ // ******************************************************* //
17
+ // Base / Config Types //
18
+ // ******************************************************* //
34
19
 
35
- type CounterCallback = {
36
- (state:CounterState):void;
37
- };
38
-
39
- export type CounterState = {
40
- value:number,
41
- labels:{}
20
+ export type MetricsReference = {
21
+ metricsEnabled: boolean,
22
+ metricsReference: string
42
23
  }
43
24
 
44
- export interface CounterMetricConfig extends MetricConfig {
45
-
25
+ export interface MetricsConfig {
26
+ id: string;
46
27
  }
47
28
 
48
- export class CounterMetric extends Metric<CounterMetricConfig>{
49
- private counter: Counter;
50
- private internalCounter: Counter.Internal;
51
- private subscribers:{[key:string]:CounterCallback} = {};
52
- constructor(id:string, config:CounterMetricConfig, registry:client.Registry){
53
- super(config);
54
-
55
- this.counter = new Counter({
56
- name: id,
57
- help: (config.metricdescription) ? config.metricdescription: config.metricname,
58
- labelNames: Object.keys(this.labels()),
59
- registers: [registry]
60
- });
61
- this.counter.reset();
62
- this.internalCounter = this.counter.labels(this.labels() as any);
63
- }
64
-
65
- public inc():CounterMetric{
66
- this.internalCounter.inc();
67
- this.get().then(state => Object.values(this.subscribers).forEach(callback => callback(state)));
68
- return this;
69
- }
70
-
71
- public async get():Promise<CounterState> {
72
- return this.counter.get().then(t => t.values[0]);
73
- }
74
-
75
- public reset():void{
76
- this.counter.reset();
77
- }
78
-
79
- public subscribe(node:BaseNode<BaseNodeConfig>, callback:CounterCallback){
80
- this.subscribers[node.id()] = callback;
81
- }
82
-
83
- public unsubscribe(node:BaseNode<BaseNodeConfig>){
84
- delete this.subscribers[node.id()];
85
- }
29
+ export interface NodeReference {
30
+ name: string;
31
+ type: string;
32
+ flow: string;
33
+ id: string;
86
34
  }
87
35
 
88
- // **************************************************** //
89
- // Gauge //
90
- // **************************************************** //
91
- type GaugeCallback = {
92
- (state:GaugeState):void;
93
- };
94
-
95
- export interface GaugeState {
96
- value: number
36
+ export interface MetricConfig {
37
+ node: NodeReference;
38
+ metricname: string;
39
+ metricdescription?: string;
97
40
  }
98
41
 
99
- export interface GaugeMetricConfig extends MetricConfig {
100
-
42
+ interface Labels {
43
+ flow: string;
44
+ type: string;
45
+ name: string;
46
+ id: string;
47
+ metric: string;
101
48
  }
102
49
 
103
- export class GaugeMetric extends Metric<GaugeMetricConfig>{
104
- private collector: number = 0;
105
- private gauge:Gauge.Internal<string>;
106
- private subscribers:{[key:string]:GaugeCallback} = {};
107
-
108
- constructor(id:string, config:GaugeMetricConfig, registry:client.Registry){
109
- super(config);
110
- let _this = this;
111
-
112
- this.gauge = new client.Gauge({
113
- name: id,
114
- help: (config.metricdescription) ? config.metricdescription: config.metricname,
115
- registers: [registry],
116
- labelNames: Object.keys(this.labels()),
117
- // this function is called every time the stats are collected.
118
- // the response value needs to be set.
119
- collect() { this.labels(_this.labels() as {}).set(_this.collector); }
120
- })
121
- }
122
-
123
- public inc():GaugeMetric{
124
- this.collector+=1;
125
- let state:GaugeState = this.get();
126
- Object.values(this.subscribers).forEach(sub => sub(state));
127
- return this;
128
- }
129
-
130
- public dec():GaugeMetric{
131
- this.collector-=1;
132
- let state:GaugeState = this.get();
133
- Object.values(this.subscribers).forEach(sub => sub(state));
134
- return this;
135
- }
136
-
137
- public get():GaugeState {
138
- return {
139
- value:this.collector,
50
+ export abstract class Metric<ConfigType extends MetricConfig> {
51
+ private _config: ConfigType;
52
+ private _labels: Labels;
53
+
54
+ protected constructor(config: ConfigType) {
55
+ this._config = config;
56
+ this._labels = {
57
+ flow: config.node.flow,
58
+ type: config.node.type,
59
+ name: config.node.name,
60
+ id: config.node.id,
61
+ metric: config.metricname
62
+ };
140
63
  }
141
- }
142
-
143
- public reset():void {
144
- this.collector = 0;
145
- }
146
64
 
147
- public subscribe(node:BaseNode<BaseNodeConfig>, callback:GaugeCallback){
148
- this.subscribers[node.id()] = callback;
149
- }
150
-
151
- public unsubscribe(node:BaseNode<BaseNodeConfig>){
152
- delete this.subscribers[node.id()];
153
- }
65
+ public labels(): Labels { return this._labels; }
66
+ public config(): ConfigType { return this._config; }
154
67
  }
155
68
 
156
- // **************************************************** //
157
- // Histogram //
158
- // **************************************************** //
159
-
160
- type HistogramCallback = {
161
- (state:HistogramState):void;
162
- };
69
+ // ******************************************************* //
70
+ // Counter //
71
+ // ******************************************************* //
163
72
 
164
- export interface DefaultBucketConfig{
73
+ export type CounterState = { value: number, labels: {} }
74
+ export type CounterCallback = (state: CounterState) => void;
75
+ export interface CounterMetricConfig extends MetricConfig { }
165
76
 
77
+ export interface CounterMetric {
78
+ inc(): this;
79
+ reset(): void;
80
+ get(): Promise<CounterState>;
81
+ subscribe(node: BaseNode<BaseNodeConfig>, callback: CounterCallback): void;
82
+ unsubscribe(node: BaseNode<BaseNodeConfig>): void;
166
83
  }
167
84
 
168
- export interface ManualBucketConfig{
169
- intervals:number[];
85
+ export class DoNothingCounterMetric implements CounterMetric {
86
+ public inc(): this { return this; }
87
+ public reset(): void {}
88
+ public get(): Promise<CounterState> { return Promise.resolve({ value: 0, labels: {} }); }
89
+ public subscribe(_node: BaseNode<BaseNodeConfig>, _callback: CounterCallback): void {}
90
+ public unsubscribe(_node: BaseNode<BaseNodeConfig>): void {}
170
91
  }
171
92
 
172
- export interface LinearBucketConfig{
173
- start:number;
174
- interval:number;
175
- count:number;
93
+ // ******************************************************* //
94
+ // Gauge //
95
+ // ******************************************************* //
96
+
97
+ export type GaugeState = { value: number }
98
+ export type GaugeCallback = (state: GaugeState) => void;
99
+ export interface GaugeMetricConfig extends MetricConfig { }
100
+
101
+ export interface GaugeMetric {
102
+ inc(): this;
103
+ dec(): this;
104
+ reset(): void;
105
+ get(): GaugeState;
106
+ subscribe(node: BaseNode<BaseNodeConfig>, callback: GaugeCallback): void;
107
+ unsubscribe(node: BaseNode<BaseNodeConfig>): void;
176
108
  }
177
109
 
178
- export interface ExponentialBucketConfig{
179
- start:number;
180
- factor:number;
181
- count:number;
110
+ export class DoNothingGaugeMetric implements GaugeMetric {
111
+ public inc(): this { return this; }
112
+ public dec(): this { return this; }
113
+ public reset(): void {}
114
+ public get(): GaugeState { return { value: 0 }; }
115
+ public subscribe(_node: BaseNode<BaseNodeConfig>, _callback: GaugeCallback): void {}
116
+ public unsubscribe(_node: BaseNode<BaseNodeConfig>): void {}
182
117
  }
183
118
 
184
- export interface HistogramMetricConfig extends MetricConfig {
185
- buckettype:BucketType,
186
- bucketconfig:DefaultBucketConfig|ManualBucketConfig|LinearBucketConfig|ExponentialBucketConfig
187
- }
119
+ // ******************************************************* //
120
+ // Histogram //
121
+ // ******************************************************* //
188
122
 
189
- export enum BucketType{
190
- default = "default",
191
- manual = "manual",
192
- linear = "linear",
193
- exponential = "exponential"
194
- }
195
-
196
- export class HistogramMetric extends Metric<HistogramMetricConfig> {
197
-
198
- private histogram: client.Histogram<string>;
199
- private subscribers:{[key:string]:HistogramCallback} = {};
200
-
201
- constructor(id:string, config:HistogramMetricConfig, registry:Registry){
202
- super(config);
203
-
204
- // Build the Histogram Config.
205
- let histogramConfig:HistogramConfiguration<string> = {
206
- name: id,
207
- help: (config.metricdescription) ? config.metricdescription: config.metricname,
208
- labelNames: Object.keys(this.labels()),
209
- registers:[registry]
210
- }
211
-
212
- if(config.buckettype === BucketType.default){
213
- let bucketConfig = (config.bucketconfig as DefaultBucketConfig);
214
- // do nothing
215
- }
216
- if(config.buckettype === BucketType.manual){
217
- let bucketConfig = (config.bucketconfig as ManualBucketConfig);
218
- histogramConfig.buckets = bucketConfig.intervals;
219
- }
220
- if(config.buckettype === BucketType.linear){
221
- let bucketConfig = (config.bucketconfig as LinearBucketConfig);
222
- histogramConfig.buckets = client.linearBuckets(bucketConfig.start, bucketConfig.interval, bucketConfig.count);
223
- }
224
- if(config.buckettype === BucketType.exponential){
225
- let bucketConfig = (config.bucketconfig as ExponentialBucketConfig);
226
- histogramConfig.buckets = client.exponentialBuckets(bucketConfig.start, bucketConfig.factor, bucketConfig.count);
227
- }
228
-
229
- this.histogram = new Histogram(histogramConfig);
230
- this.histogram.zero(this.labels() as any);
231
- }
123
+ export type HistogramState = { average(): number | undefined; }
124
+ export type HistogramCallback = (state: HistogramState) => void;
232
125
 
233
- public observe(value:number){
234
- this.histogram.labels(this.labels() as any).observe(value);
235
- this.get().then(status => Object.values(this.subscribers).forEach(subscriber => subscriber(status)));
236
- }
237
-
238
- public async get():Promise<HistogramState>{
239
- return this.histogram.get().then(t => new HistogramState(t.values));
240
- }
126
+ export enum BucketType {
127
+ default = "default",
128
+ manual = "manual",
129
+ linear = "linear",
130
+ exponential = "exponential"
131
+ }
241
132
 
242
- public subscribe(node:BaseNode<BaseNodeConfig>, callback:HistogramCallback){
243
- this.subscribers[node.id()] = callback;
244
- }
133
+ export interface DefaultBucketConfig { }
134
+ export interface ManualBucketConfig { intervals: number[]; }
135
+ export interface LinearBucketConfig { start: number; interval: number; count: number; }
136
+ export interface ExponentialBucketConfig { start: number; factor: number; count: number; }
245
137
 
246
- public unsubscribe(node:BaseNode<BaseNodeConfig>){
247
- delete this.subscribers[node.id()];
248
- }
138
+ export interface HistogramMetricConfig extends MetricConfig {
139
+ buckettype: BucketType;
140
+ bucketconfig: DefaultBucketConfig | ManualBucketConfig | LinearBucketConfig | ExponentialBucketConfig;
249
141
  }
250
142
 
251
- class HistogramState {
252
- private _values: { labels: any; value: number; metricName?:string }[];
253
- private _average: number | undefined;
254
-
255
- constructor(values:{labels:any, value:number, metricName?:string}[]){
256
- this._values = values;
257
-
258
- let sum = this._values.find(value => value.metricName?.endsWith("_sum"))?.value;
259
- let count = this._values.find(value => value.metricName?.endsWith("_count"))?.value;
260
- this._average = (sum && count) ? sum/count : undefined;
261
- }
262
-
263
- public average():number|undefined{
264
- return this._average;
265
- }
143
+ export interface HistogramMetric {
144
+ observe(value: number): void;
145
+ subscribe(node: BaseNode<BaseNodeConfig>, callback: HistogramCallback): void;
146
+ unsubscribe(node: BaseNode<BaseNodeConfig>): void;
266
147
  }
267
148
 
268
- // **************************************************** //
269
- // Summary //
270
- // **************************************************** //
271
-
272
- interface PercentileConfig{}
273
-
274
- class SummaryState {
275
- private _values: { labels: any; value: number; metricName?:string }[];
276
- private _average: number | undefined;
277
-
278
- constructor(values:{labels:any, value:number, metricName?:string}[]){
279
- this._values = values;
280
-
281
- let sum = this._values.find(value => value.metricName?.endsWith("_sum"))?.value;
282
- let count = this._values.find(value => value.metricName?.endsWith("_count"))?.value;
283
- this._average = (sum && count) ? sum/count : undefined;
284
- }
285
-
286
- public average():number|undefined{
287
- return this._average;
288
- }
149
+ export class DoNothingHistogramMetric implements HistogramMetric {
150
+ public observe(_value: number): void {}
151
+ public subscribe(_node: BaseNode<BaseNodeConfig>, _callback: HistogramCallback): void {}
152
+ public unsubscribe(_node: BaseNode<BaseNodeConfig>): void {}
289
153
  }
290
154
 
291
- type SummaryCallback = (state:SummaryState) => void
155
+ // ******************************************************* //
156
+ // Summary //
157
+ // ******************************************************* //
292
158
 
293
- export interface DefaultPercentileConfig extends PercentileConfig {
294
- }
159
+ export type SummaryState = { average(): number | undefined; }
160
+ export type SummaryCallback = (state: SummaryState) => void;
295
161
 
296
- export interface ManualPercentileConfig extends PercentileConfig {
297
- percentiles:number[]
162
+ export enum PercentileType {
163
+ default = "default",
164
+ manual = "manual"
298
165
  }
299
166
 
167
+ export interface DefaultPercentileConfig { }
168
+ export interface ManualPercentileConfig { percentiles: number[]; }
169
+
300
170
  export interface SummaryMetricConfig extends MetricConfig {
301
- percentileType:PercentileType,
302
- percentileConfig:PercentileConfig
171
+ percentileType: PercentileType;
172
+ percentileConfig: DefaultPercentileConfig | ManualPercentileConfig;
303
173
  }
304
174
 
305
- export enum PercentileType {
306
- default = "default",
307
- manual = "manual"
175
+ export interface SummaryMetric {
176
+ observe(value: number): void;
177
+ subscribe(node: BaseNode<BaseNodeConfig>, callback: SummaryCallback): void;
178
+ unsubscribe(node: BaseNode<BaseNodeConfig>): void;
308
179
  }
309
180
 
310
- export class SummaryMetric extends Metric<SummaryMetricConfig> {
311
- private summary: client.Summary;
312
- private subscribers:{[key:string]:SummaryCallback} = {};
313
-
314
- constructor(id:string, config:SummaryMetricConfig, registry:Registry){
315
- super(config);
316
-
317
- let summaryConfig:SummaryConfiguration<string> = {
318
- name: id,
319
- help: (config.metricdescription) ? config.metricdescription: config.metricname,
320
- labelNames: Object.keys(this.labels()),
321
- registers:[registry],
322
- };
323
-
324
- if(config.percentileType === PercentileType.default){
325
- // do nothing.
326
- let perceintileConfig:DefaultPercentileConfig = config.percentileConfig as DefaultPercentileConfig;
327
- summaryConfig.percentiles = [0.01, 0.1, 0.9, 0.99];
328
- }
329
-
330
- if(config.percentileType === PercentileType.manual){
331
- let percentilesConfig:ManualPercentileConfig = config.percentileConfig as ManualPercentileConfig;
332
- summaryConfig.percentiles = percentilesConfig.percentiles;
333
- }
334
-
335
- this.summary = new Summary(summaryConfig);
336
- //this.summary.zero(this._labels as any);
337
- }
338
-
339
- public observe(value:number){
340
- this.summary.labels(this.labels() as any).observe(value);
341
- this.get().then(status => Object.values(this.subscribers).forEach(subscriber => subscriber(status)));
342
- }
343
-
344
- public async get():Promise<SummaryState>{
345
- return this.summary.get().then(s => new SummaryState(s.values));
346
- }
347
-
348
- public subscribe(node:BaseNode<BaseNodeConfig>, callback:SummaryCallback){
349
- this.subscribers[node.id()] = callback;
350
- }
351
-
352
- public unsubscribe(node:BaseNode<BaseNodeConfig>){
353
- delete this.subscribers[node.id()];
354
- }
181
+ export class DoNothingSummaryMetric implements SummaryMetric {
182
+ public observe(_value: number): void {}
183
+ public subscribe(_node: BaseNode<BaseNodeConfig>, _callback: SummaryCallback): void {}
184
+ public unsubscribe(_node: BaseNode<BaseNodeConfig>): void {}
355
185
  }
356
186
 
357
- // **************************************************** //
358
- // COMMONS //
359
- // **************************************************** //
360
- interface Labels {
361
- flow:string;
362
- type:string;
363
- name:string;
364
- id:string;
365
- metric:string;
366
- }
187
+ // ******************************************************* //
188
+ // MetricsContainer //
189
+ // ******************************************************* //
367
190
 
368
- export class MetricsContainer {
369
- private _config: MetricsConfig;
370
- private _registry: client.Registry;
371
-
372
- private counters:{[key:string]:CounterMetric} = {};
373
- private histograms:{[key:string]:HistogramMetric} = {};
374
- private gauges:{[key:string]:GaugeMetric} = {};
375
- private summaries:{[key:string]:SummaryMetric} = {};
376
-
377
- constructor(config:MetricsConfig){
378
- this._config = config;
379
-
380
- // create a registry with the config.
381
- this._registry = new client.Registry();
382
-
383
- }
384
-
385
- close() {
386
- this._registry.clear();
387
- }
388
-
389
- public registry():client.Registry{
390
- return this._registry;
391
- }
392
-
393
- // return true if the two configurations are not the same.
394
- hasChanged(config: MetricsConfig): boolean {
395
- return !(
396
- this._config.metricsPath === config.metricsPath &&
397
- this._config.metricsPort === config.metricsPort
398
- );
399
- }
400
-
401
- public counter(config: CounterMetricConfig): CounterMetric{
402
- const counterId = `counter_${config.node.id}`;
403
- let counter:CounterMetric = this.counters[counterId];
404
- if(counter){
405
- // check if the config has changed much.
406
- if(!deepEqual(counter.config(), config)){
407
- this._registry.removeSingleMetric(counterId);
408
- this.counters[counterId] = (counter = new CounterMetric(counterId, config, this._registry));
409
- }
410
- }
411
- else{
412
- this.counters[counterId] = (counter = new CounterMetric(counterId, config, this._registry));
413
- }
414
-
415
- return counter;
416
- }
417
-
418
- public gauge(config: GaugeMetricConfig): GaugeMetric {
419
- var gaugeId = `gauge_${config.node.id}`;
420
- var gauge:GaugeMetric = this.gauges[gaugeId];
421
- if(gauge){
422
- // check if the config has changed much.
423
- if(!deepEqual(gauge.config(), config)){
424
- this._registry.removeSingleMetric(gaugeId);
425
- this.gauges[gaugeId] = (gauge = new GaugeMetric(gaugeId, config, this._registry));
426
- }
427
- }
428
- else{
429
- this.gauges[gaugeId] = (gauge = new GaugeMetric(gaugeId, config, this._registry));
430
- }
431
- return gauge;
432
- }
433
-
434
- public histogram(config: HistogramMetricConfig): HistogramMetric {
435
- const histogramId = `histogram_${config.node.id}`;
436
- let histogram:HistogramMetric = this.histograms[histogramId];
437
- if(histogram){
438
- // check if the config has changed much.
439
- if(!deepEqual(histogram.config(), config)){
440
- this._registry.removeSingleMetric(histogramId);
441
- this.histograms[histogramId] = (histogram = new HistogramMetric(histogramId, config, this._registry));
442
- }
191
+ export abstract class MetricsContainer {
192
+ public abstract supports(capability: MetricCapability): boolean;
193
+ public abstract hasChanged(config: MetricsConfig): boolean;
194
+ public abstract close(): void;
195
+
196
+ // Default implementations warn and return DoNothing - providers override what they support.
197
+ public counter(_config: CounterMetricConfig): CounterMetric {
198
+ console.warn(`[PluginCore] MetricsContainer: Counter is not supported by this provider.`);
199
+ return new DoNothingCounterMetric();
443
200
  }
444
- else{
445
- this.histograms[histogramId] = (histogram = new HistogramMetric(histogramId, config, this._registry));
201
+ public gauge(_config: GaugeMetricConfig): GaugeMetric {
202
+ console.warn(`[PluginCore] MetricsContainer: Gauge is not supported by this provider.`);
203
+ return new DoNothingGaugeMetric();
446
204
  }
447
-
448
- return histogram;
449
- }
450
-
451
- public summary(config: SummaryMetricConfig): SummaryMetric {
452
- const summaryId = `summary_${config.node.id}`;
453
- let summary:SummaryMetric = this.summaries[summaryId];
454
- if(summary){
455
- if(!deepEqual(summary.config(), config)){
456
- this._registry.removeSingleMetric(summaryId);
457
- this.summaries[summaryId] = (summary = new SummaryMetric(summaryId, config, this._registry));
458
- }
205
+ public histogram(_config: HistogramMetricConfig): HistogramMetric {
206
+ console.warn(`[PluginCore] MetricsContainer: Histogram is not supported by this provider.`);
207
+ return new DoNothingHistogramMetric();
459
208
  }
460
- else{
461
- this.summaries[summaryId] = (summary = new SummaryMetric(summaryId, config, this._registry));
209
+ public summary(_config: SummaryMetricConfig): SummaryMetric {
210
+ console.warn(`[PluginCore] MetricsContainer: Summary is not supported by this provider.`);
211
+ return new DoNothingSummaryMetric();
462
212
  }
213
+ }
463
214
 
464
- return summary;
465
- }
215
+ // Silent DoNothing - returned when no provider is installed. Does not warn since this is expected.
216
+ export class DoNothingMetricsContainer extends MetricsContainer {
217
+ public supports(_capability: MetricCapability): boolean { return false; }
218
+ public hasChanged(_config: MetricsConfig): boolean { return false; }
219
+ public close(): void {}
220
+ public counter(_config: CounterMetricConfig): CounterMetric { return new DoNothingCounterMetric(); }
221
+ public gauge(_config: GaugeMetricConfig): GaugeMetric { return new DoNothingGaugeMetric(); }
222
+ public histogram(_config: HistogramMetricConfig): HistogramMetric { return new DoNothingHistogramMetric(); }
223
+ public summary(_config: SummaryMetricConfig): SummaryMetric { return new DoNothingSummaryMetric(); }
466
224
  }
467
225
 
226
+ // ******************************************************* //
227
+ // MetricsService //
228
+ // ******************************************************* //
468
229
 
469
- export class MetricsService extends BaseService {
230
+ const _GLOBAL_METRICS_KEY = '__plugincore_metrics__';
470
231
 
471
- private static metrics:{[key:string]:MetricsContainer} = {};
472
- private red!: NodeAPI<NodeAPISettingsWithData>;
473
-
474
- public constructor(){
475
- super("MetricsService");
476
- }
477
-
478
- public init(red: NodeAPI<NodeAPISettingsWithData>): void {
479
- this.red = red;
480
- }
481
-
482
- public deinit(red: NodeAPI<NodeAPISettingsWithData>): void {
483
- }
484
-
485
- public async onDeploy(deployment: FlowDeployment): Promise<void> {
486
- // get the FlowElements.
487
- let configNodes:{[key:string]:MetricsConfig} = {};
488
- deployment.flows
489
- .filter(element => element.type === "MetricsConfigNode")
490
- .forEach(element => configNodes[element.id] = element as any as MetricsConfig);
491
-
492
- // retire metrics that have been removed or that have changed.
493
- Object.entries(MetricsService.metrics)
494
- .filter(([id, metric]) => !configNodes[id] || metric.hasChanged(configNodes[id]))
495
- .forEach(([id, metric]) => {
496
- delete MetricsService.metrics[id];
497
- metric.close();
498
- });
499
-
500
- // create the new metrics.
501
- Object.entries(configNodes)
502
- .filter(([id, metricConfig]) => !MetricsService.metrics[id])
503
- .forEach(([id, metricConfig]) => {
504
- MetricsService.metrics[id] = new MetricsContainer(metricConfig);
505
- })
506
-
507
- return Promise.resolve();
508
- }
509
-
510
- public static get(reference:MetricsReference):MetricsContainer{
511
- return MetricsService.metrics[reference.metricsReference];
512
- }
513
-
514
- static override getServiceDescriptor():ServiceDescriptor {
515
- return new ServiceDescriptor(
516
- "@theotherwillembotha/metricsservice",
517
- "MetricsService",
518
- "services-plugin",
519
- "@theotherwillembotha/node-red-plugincore",
520
- MetricsService
521
- );
522
- }
232
+ function getMetricsStore(): { [key: string]: MetricsContainer } {
233
+ if (!(global as any)[_GLOBAL_METRICS_KEY]) (global as any)[_GLOBAL_METRICS_KEY] = {};
234
+ return (global as any)[_GLOBAL_METRICS_KEY];
523
235
  }
524
236
 
525
- export interface MetricsConfig {
526
- id:string,
527
- metricsPath:string,
528
- metricsPort:number,
529
- }
237
+ @ServiceDescription({
238
+ id: "@theotherwillembotha/metricsservice",
239
+ sourceFile: "@theotherwillembotha/node-red-plugincore"
240
+ })
241
+ export class MetricsService extends BaseService {
530
242
 
531
- export interface NodeReference {
532
- name: string;
533
- type: string;
534
- flow: string;
535
- id: string;
536
- }
243
+ private red!: NodeAPI<NodeAPISettingsWithData>;
537
244
 
538
- export interface MetricConfig{
539
- node:NodeReference;
540
- metricname: string;
541
- metricdescription?: string;
542
- }
245
+ public constructor() {
246
+ super("MetricsService");
247
+ }
248
+
249
+ public init(red: NodeAPI<NodeAPISettingsWithData>): void {
250
+ this.red = red;
251
+ }
543
252
 
544
- /*
545
- Quick explanation of how this is supposed to work:
253
+ public deinit(_red: NodeAPI<NodeAPISettingsWithData>): void {}
546
254
 
547
- when you deploy a new metricConfig, it should create an instance of "Metric" in the backend.
548
- this instance should survive flow redeployments, but should be removed if the flow that
549
- gets deployed no longer contains the metric. The idea is to not have the metrics reset between
550
- flow deployments or to have the Metric / Repository become unavialable between deployments.
255
+ public async onDeploy(deployment: FlowDeployment): Promise<void> {
256
+ const store = getMetricsStore();
257
+ const flowIds = new Set(deployment.flows.map(e => e.id));
551
258
 
552
- users should be able to access it in this way:
259
+ Object.entries(store)
260
+ .filter(([id]) => !flowIds.has(id))
261
+ .forEach(([id, container]) => {
262
+ delete store[id];
263
+ container.close();
264
+ });
553
265
 
554
- MetricsService.get(<MetricReference>).createCounter(<counterConfig>), though this is mostly just intended for use in the MetricConfigClass.
266
+ return Promise.resolve();
267
+ }
555
268
 
556
- Normally, a handle on the MetricsService should be gained by pulling a reference to the MetricsConfigNode, eg
557
- this._metrics = (NodeManager.RED.nodes.getNode(config.metricsReference) as any).node().metrics();
558
- this._counter = this._metrics.counter(this, "messages", "Number of messages that have passed through this node");
269
+ /**
270
+ * Called by MetricsConfigNode subclasses on construction.
271
+ * Preserves an existing container across redeploys if the config has not changed
272
+ * (so counters/gauges are not reset unnecessarily).
273
+ */
274
+ public static register(id: string, config: MetricsConfig, factory: () => MetricsContainer): void {
275
+ const store = getMetricsStore();
276
+ const existing = store[id];
277
+ if (existing && !existing.hasChanged(config)) return;
278
+ if (existing) existing.close();
279
+ store[id] = factory();
280
+ }
559
281
 
560
- */
282
+ public static get(reference: MetricsReference): MetricsContainer {
283
+ return getMetricsStore()[reference.metricsReference] ?? new DoNothingMetricsContainer();
284
+ }
285
+ }