tabletcommand-backend-models 7.4.90 → 7.4.91

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 (35) hide show
  1. package/README.md +68 -1
  2. package/build/index.js +2 -0
  3. package/build/index.js.map +1 -1
  4. package/build/models/status-beacon-transition.js +51 -0
  5. package/build/models/status-beacon-transition.js.map +1 -0
  6. package/build/models/status-beacon.js +174 -0
  7. package/build/models/status-beacon.js.map +1 -0
  8. package/build/test/0index.js +2 -0
  9. package/build/test/0index.js.map +1 -1
  10. package/build/test/mock.js +64 -0
  11. package/build/test/mock.js.map +1 -1
  12. package/build/test/status-beacon.js +112 -0
  13. package/build/test/status-beacon.js.map +1 -0
  14. package/build/types/status-beacon.js +3 -0
  15. package/build/types/status-beacon.js.map +1 -0
  16. package/definitions/index.d.ts +12 -0
  17. package/definitions/index.d.ts.map +1 -1
  18. package/definitions/models/status-beacon-transition.d.ts +13 -0
  19. package/definitions/models/status-beacon-transition.d.ts.map +1 -0
  20. package/definitions/models/status-beacon.d.ts +13 -0
  21. package/definitions/models/status-beacon.d.ts.map +1 -0
  22. package/definitions/test/mock.d.ts +62 -0
  23. package/definitions/test/mock.d.ts.map +1 -1
  24. package/definitions/test/status-beacon.d.ts +2 -0
  25. package/definitions/test/status-beacon.d.ts.map +1 -0
  26. package/definitions/types/status-beacon.d.ts +72 -0
  27. package/definitions/types/status-beacon.d.ts.map +1 -0
  28. package/package.json +1 -1
  29. package/src/index.ts +4 -0
  30. package/src/models/status-beacon-transition.ts +60 -0
  31. package/src/models/status-beacon.ts +193 -0
  32. package/src/test/0index.ts +2 -0
  33. package/src/test/mock.ts +66 -0
  34. package/src/test/status-beacon.ts +121 -0
  35. package/src/types/status-beacon.ts +90 -0
@@ -0,0 +1,193 @@
1
+ import {
2
+ MongooseModule,
3
+ currentDate,
4
+ } from "../helpers";
5
+ import { Model } from "mongoose";
6
+ import {
7
+ StatusBeaconType,
8
+ QueueDepthsType,
9
+ TCSendHealthType,
10
+ WriteBackHealthType,
11
+ SyncHealthType,
12
+ } from "../types/status-beacon";
13
+
14
+ export interface StatusBeacon extends StatusBeaconType { }
15
+
16
+ export default async function StatusBeaconModule(mongoose: MongooseModule) {
17
+ const { Schema } = mongoose;
18
+
19
+ const QueueDepths = new Schema<QueueDepthsType>({
20
+ incident: { type: Number, default: 0 },
21
+ status: { type: Number, default: 0 },
22
+ vehicle: { type: Number, default: 0 },
23
+ total: { type: Number, default: 0 },
24
+ }, { _id: false, id: false });
25
+
26
+ const TCSendHealth = new Schema<TCSendHealthType>({
27
+ lastSuccessfulIncidentSend: { type: Date },
28
+ lastSuccessfulUnitStatusSend: { type: Date },
29
+ sendFailuresDelta: { type: Number, default: 0 },
30
+ lastSendError: { type: String, default: "" },
31
+ }, { _id: false, id: false });
32
+
33
+ const WriteBackHealth = new Schema<WriteBackHealthType>({
34
+ attemptsDelta: { type: Number, default: 0 },
35
+ failuresDelta: { type: Number, default: 0 },
36
+ lastSuccessfulWriteBack: { type: Date },
37
+ lastError: { type: String, default: "" },
38
+ }, { _id: false, id: false });
39
+
40
+ const SyncHealth = new Schema<SyncHealthType>({
41
+ lastCacheRefresh: { type: Date },
42
+ cacheAgeSeconds: { type: Number, default: 0 },
43
+ consecutiveEmptyPolls: { type: Number, default: 0 },
44
+ lastPollChanges: { type: Number, default: 0 },
45
+ incidentCacheGrowthDelta: { type: Number, default: 0 },
46
+ }, { _id: false, id: false });
47
+
48
+ const modelSchema = new Schema<StatusBeaconType>({
49
+ _id: {
50
+ type: Schema.Types.ObjectId,
51
+ auto: true,
52
+ },
53
+ departmentId: {
54
+ type: String,
55
+ default: "",
56
+ required: true,
57
+ },
58
+ // Identity
59
+ integration: {
60
+ type: String,
61
+ default: "",
62
+ required: true,
63
+ },
64
+ interfaceStartTime: {
65
+ type: Date,
66
+ default: currentDate,
67
+ },
68
+ // Health State
69
+ healthState: {
70
+ type: String,
71
+ default: "",
72
+ },
73
+ triggerReason: {
74
+ type: String,
75
+ default: "",
76
+ },
77
+ consecutiveFailures: {
78
+ type: Number,
79
+ default: 0,
80
+ },
81
+ consecutiveSuccesses: {
82
+ type: Number,
83
+ default: 0,
84
+ },
85
+ totalFailuresSinceLastHealthy: {
86
+ type: Number,
87
+ default: 0,
88
+ },
89
+ outageStartTime: {
90
+ type: Date,
91
+ },
92
+ // Cycle Metrics
93
+ pollCycle: {
94
+ type: Number,
95
+ default: 0,
96
+ },
97
+ lastPollMs: {
98
+ type: Number,
99
+ default: 0,
100
+ },
101
+ activeIncidents: {
102
+ type: Number,
103
+ default: 0,
104
+ },
105
+ activeUnits: {
106
+ type: Number,
107
+ default: 0,
108
+ },
109
+ // Data Flow Deltas
110
+ incidentsDelta: {
111
+ type: Number,
112
+ default: 0,
113
+ },
114
+ unitStatusDelta: {
115
+ type: Number,
116
+ default: 0,
117
+ },
118
+ // Queue Depths
119
+ queueDepths: {
120
+ type: QueueDepths,
121
+ default: () => ({}),
122
+ },
123
+ // Auth Token Health
124
+ tokenAgeSecs: {
125
+ type: Number,
126
+ },
127
+ tokenExpiresAt: {
128
+ type: Date,
129
+ },
130
+ // TC Send Health
131
+ tCSend: {
132
+ type: TCSendHealth,
133
+ default: () => ({}),
134
+ },
135
+ // Write-back Health
136
+ writeBack: {
137
+ type: WriteBackHealth,
138
+ default: () => ({}),
139
+ },
140
+ // Sync Health
141
+ sync: {
142
+ type: SyncHealth,
143
+ default: () => ({}),
144
+ },
145
+ // Debug & Config
146
+ debugPushActive: {
147
+ type: Boolean,
148
+ default: false,
149
+ },
150
+ lastConfigSave: {
151
+ type: Date,
152
+ },
153
+ lastConfigSaveFile: {
154
+ type: String,
155
+ default: "",
156
+ },
157
+ // Error & Version
158
+ lastError: {
159
+ type: String,
160
+ default: "",
161
+ },
162
+ interfaceVersion: {
163
+ type: String,
164
+ default: "",
165
+ },
166
+ cadVersion: {
167
+ type: String,
168
+ default: "",
169
+ },
170
+ unifiedVersion: {
171
+ type: String,
172
+ default: "",
173
+ },
174
+ }, {
175
+ autoIndex: false,
176
+ timestamps: true,
177
+ });
178
+
179
+ modelSchema.index({ departmentId: 1, integration: 1 }, {
180
+ name: "departmentId_1_integration_1",
181
+ unique: true,
182
+ });
183
+ modelSchema.index({ healthState: 1 }, { name: "healthState_1" });
184
+ modelSchema.index({ triggerReason: 1 }, { name: "triggerReason_1" });
185
+ modelSchema.index({ "queueDepths.total": 1 }, { name: "queueDepths_total_1" });
186
+ modelSchema.index({ "tCSend.sendFailuresDelta": 1 }, { name: "tCSend_sendFailuresDelta_1" });
187
+ modelSchema.index({ "writeBack.failuresDelta": 1 }, { name: "writeBack_failuresDelta_1" });
188
+ modelSchema.index({ "sync.consecutiveEmptyPolls": 1 }, { name: "sync_consecutiveEmptyPolls_1" });
189
+
190
+ return mongoose.model<StatusBeacon>("StatusBeacon", modelSchema, "massive_status_beacon", { overwriteModels: true });
191
+ }
192
+
193
+ export interface StatusBeaconModel extends Model<StatusBeacon> { }
@@ -67,6 +67,8 @@ describe(" Models", function() {
67
67
  assert.isFunction(models.RemoteLogStream, "Missing RemoteLogStream");
68
68
  assert.isFunction(models.Session, "Missing Session");
69
69
  assert.isFunction(models.SAML, "Missing SAML");
70
+ assert.isFunction(models.StatusBeacon, "Missing StatusBeacon");
71
+ assert.isFunction(models.StatusBeaconTransition, "Missing StatusBeaconTransition");
70
72
  assert.isFunction(models.SMSLog, "Missing SMSLog");
71
73
  assert.isFunction(models.SMTPUnhandled, "Missing SMTPUnhandled");
72
74
  assert.isFunction(models.Template, "Missing Template");
package/src/test/mock.ts CHANGED
@@ -1504,6 +1504,70 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
1504
1504
  sendNotification: false
1505
1505
  };
1506
1506
 
1507
+ const statusBeacon = {
1508
+ _id: new mongoose.Types.ObjectId(),
1509
+ departmentId,
1510
+ integration: "test-cad-integration",
1511
+ interfaceStartTime: new Date(),
1512
+ healthState: "healthy",
1513
+ triggerReason: "poll_success",
1514
+ consecutiveFailures: 0,
1515
+ consecutiveSuccesses: 5,
1516
+ totalFailuresSinceLastHealthy: 0,
1517
+ outageStartTime: undefined,
1518
+ pollCycle: 42,
1519
+ lastPollMs: 350,
1520
+ activeIncidents: 3,
1521
+ activeUnits: 12,
1522
+ incidentsDelta: 1,
1523
+ unitStatusDelta: 2,
1524
+ queueDepths: {
1525
+ incident: 0,
1526
+ status: 0,
1527
+ vehicle: 0,
1528
+ total: 0,
1529
+ },
1530
+ tokenAgeSecs: 1800,
1531
+ tokenExpiresAt: new Date(),
1532
+ tCSend: {
1533
+ lastSuccessfulIncidentSend: new Date(),
1534
+ lastSuccessfulUnitStatusSend: new Date(),
1535
+ sendFailuresDelta: 0,
1536
+ lastSendError: "",
1537
+ },
1538
+ writeBack: {
1539
+ attemptsDelta: 0,
1540
+ failuresDelta: 0,
1541
+ lastSuccessfulWriteBack: new Date(),
1542
+ lastError: "",
1543
+ },
1544
+ sync: {
1545
+ lastCacheRefresh: new Date(),
1546
+ cacheAgeSeconds: 30,
1547
+ consecutiveEmptyPolls: 0,
1548
+ lastPollChanges: 2,
1549
+ incidentCacheGrowthDelta: 1,
1550
+ },
1551
+ debugPushActive: false,
1552
+ lastConfigSave: new Date(),
1553
+ lastConfigSaveFile: "config-v1.json",
1554
+ lastError: "",
1555
+ interfaceVersion: "1.2.3",
1556
+ cadVersion: "4.5.6",
1557
+ unifiedVersion: "7.8.9",
1558
+ };
1559
+
1560
+ const statusBeaconTransition = {
1561
+ _id: new mongoose.Types.ObjectId(),
1562
+ departmentId,
1563
+ integration: "test-cad-integration",
1564
+ triggerReason: "consecutive_failures",
1565
+ healthState: "degraded",
1566
+ consecutiveFailures: 3,
1567
+ totalFailuresSinceLastHealthy: 5,
1568
+ lastError: "connection timeout",
1569
+ };
1570
+
1507
1571
  const validationReport = {
1508
1572
  _id: new mongoose.Types.ObjectId(),
1509
1573
  departmentId: new mongoose.Types.ObjectId("56131f724143487a10000001"),
@@ -1601,6 +1665,8 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
1601
1665
  releaseNote,
1602
1666
  session,
1603
1667
  saml,
1668
+ statusBeacon,
1669
+ statusBeaconTransition,
1604
1670
  template,
1605
1671
  user,
1606
1672
  userDevice,
@@ -0,0 +1,121 @@
1
+ import { assert } from "chai";
2
+ import { describe, it, beforeEach, afterEach } from "node:test";
3
+ import * as m from "../index";
4
+ import * as config from "./config";
5
+ import mockModule from "./mock";
6
+
7
+ describe("StatusBeacon", function() {
8
+ let models: m.BackendModels, mongoose: m.MongooseModule;
9
+ let testItem: Partial<m.StatusBeacon>;
10
+ beforeEach(async function() {
11
+ const c = await m.connect(config.url);
12
+ models = c.models;
13
+ mongoose = c.mongoose;
14
+
15
+ const mock = mockModule({ mongoose });
16
+ testItem = mock.statusBeacon;
17
+ await mock.beforeEach();
18
+ });
19
+ afterEach(async function() {
20
+ await mongoose.disconnect();
21
+ });
22
+
23
+ it("is saved", async function() {
24
+ const item = new models.StatusBeacon(testItem);
25
+ const sut = await item.save();
26
+
27
+ assert.isNotNull(sut._id);
28
+ assert.equal(testItem.departmentId, sut.departmentId);
29
+ assert.equal(testItem.integration, sut.integration);
30
+ assert.equal(testItem.healthState, sut.healthState);
31
+ assert.equal(testItem.triggerReason, sut.triggerReason);
32
+ assert.equal(testItem.consecutiveFailures, sut.consecutiveFailures);
33
+ assert.equal(testItem.consecutiveSuccesses, sut.consecutiveSuccesses);
34
+ assert.equal(testItem.totalFailuresSinceLastHealthy, sut.totalFailuresSinceLastHealthy);
35
+ assert.equal(testItem.pollCycle, sut.pollCycle);
36
+ assert.equal(testItem.lastPollMs, sut.lastPollMs);
37
+ assert.equal(testItem.activeIncidents, sut.activeIncidents);
38
+ assert.equal(testItem.activeUnits, sut.activeUnits);
39
+ assert.equal(testItem.incidentsDelta, sut.incidentsDelta);
40
+ assert.equal(testItem.unitStatusDelta, sut.unitStatusDelta);
41
+ assert.equal(testItem.tokenAgeSecs, sut.tokenAgeSecs);
42
+ assert.equal(testItem.debugPushActive, sut.debugPushActive);
43
+ assert.equal(testItem.lastConfigSaveFile, sut.lastConfigSaveFile);
44
+ assert.equal(testItem.lastError, sut.lastError);
45
+ assert.equal(testItem.interfaceVersion, sut.interfaceVersion);
46
+ assert.equal(testItem.cadVersion, sut.cadVersion);
47
+ assert.equal(testItem.unifiedVersion, sut.unifiedVersion);
48
+ assert.isNotNull(sut.createdAt);
49
+ assert.isNotNull(sut.updatedAt);
50
+ });
51
+
52
+ it("saves nested queueDepths", async function() {
53
+ const item = new models.StatusBeacon(testItem);
54
+ const sut = await item.save();
55
+
56
+ assert.equal(testItem.queueDepths?.incident, sut.queueDepths.incident);
57
+ assert.equal(testItem.queueDepths?.status, sut.queueDepths.status);
58
+ assert.equal(testItem.queueDepths?.vehicle, sut.queueDepths.vehicle);
59
+ assert.equal(testItem.queueDepths?.total, sut.queueDepths.total);
60
+ });
61
+
62
+ it("saves nested tCSend health", async function() {
63
+ const item = new models.StatusBeacon(testItem);
64
+ const sut = await item.save();
65
+
66
+ assert.equal(testItem.tCSend?.sendFailuresDelta, sut.tCSend.sendFailuresDelta);
67
+ assert.equal(testItem.tCSend?.lastSendError, sut.tCSend.lastSendError);
68
+ });
69
+
70
+ it("saves nested writeBack health", async function() {
71
+ const item = new models.StatusBeacon(testItem);
72
+ const sut = await item.save();
73
+
74
+ assert.equal(testItem.writeBack?.attemptsDelta, sut.writeBack.attemptsDelta);
75
+ assert.equal(testItem.writeBack?.failuresDelta, sut.writeBack.failuresDelta);
76
+ assert.equal(testItem.writeBack?.lastError, sut.writeBack.lastError);
77
+ });
78
+
79
+ it("saves nested sync health", async function() {
80
+ const item = new models.StatusBeacon(testItem);
81
+ const sut = await item.save();
82
+
83
+ assert.equal(testItem.sync?.cacheAgeSeconds, sut.sync.cacheAgeSeconds);
84
+ assert.equal(testItem.sync?.consecutiveEmptyPolls, sut.sync.consecutiveEmptyPolls);
85
+ assert.equal(testItem.sync?.lastPollChanges, sut.sync.lastPollChanges);
86
+ assert.equal(testItem.sync?.incidentCacheGrowthDelta, sut.sync.incidentCacheGrowthDelta);
87
+ });
88
+ });
89
+
90
+ describe("StatusBeaconTransition", function() {
91
+ let models: m.BackendModels, mongoose: m.MongooseModule;
92
+ let testItem: Partial<m.StatusBeaconTransition>;
93
+ beforeEach(async function() {
94
+ const c = await m.connect(config.url);
95
+ models = c.models;
96
+ mongoose = c.mongoose;
97
+
98
+ const mock = mockModule({ mongoose });
99
+ testItem = mock.statusBeaconTransition;
100
+ await mock.beforeEach();
101
+ });
102
+ afterEach(async function() {
103
+ await mongoose.disconnect();
104
+ });
105
+
106
+ it("is saved", async function() {
107
+ const item = new models.StatusBeaconTransition(testItem);
108
+ const sut = await item.save();
109
+
110
+ assert.isNotNull(sut._id);
111
+ assert.equal(testItem.departmentId, sut.departmentId);
112
+ assert.equal(testItem.integration, sut.integration);
113
+ assert.equal(testItem.triggerReason, sut.triggerReason);
114
+ assert.equal(testItem.healthState, sut.healthState);
115
+ assert.equal(testItem.consecutiveFailures, sut.consecutiveFailures);
116
+ assert.equal(testItem.totalFailuresSinceLastHealthy, sut.totalFailuresSinceLastHealthy);
117
+ assert.equal(testItem.lastError, sut.lastError);
118
+ assert.isNotNull(sut.createdAt);
119
+ assert.isNotNull(sut.updatedAt);
120
+ });
121
+ });
@@ -0,0 +1,90 @@
1
+ import { Types } from "mongoose";
2
+
3
+ export interface QueueDepthsType {
4
+ incident: number;
5
+ status: number;
6
+ vehicle: number;
7
+ total: number;
8
+ }
9
+
10
+ export interface TCSendHealthType {
11
+ lastSuccessfulIncidentSend?: Date;
12
+ lastSuccessfulUnitStatusSend?: Date;
13
+ sendFailuresDelta: number;
14
+ lastSendError: string;
15
+ }
16
+
17
+ export interface WriteBackHealthType {
18
+ attemptsDelta: number;
19
+ failuresDelta: number;
20
+ lastSuccessfulWriteBack?: Date;
21
+ lastError: string;
22
+ }
23
+
24
+ export interface SyncHealthType {
25
+ lastCacheRefresh?: Date;
26
+ cacheAgeSeconds: number;
27
+ consecutiveEmptyPolls: number;
28
+ lastPollChanges: number;
29
+ incidentCacheGrowthDelta: number;
30
+ }
31
+
32
+ export interface StatusBeaconType {
33
+ _id: Types.ObjectId;
34
+ departmentId: string;
35
+ // Identity
36
+ integration: string;
37
+ interfaceStartTime: Date; // UTC time the integration service process started; never resets — use for uptime
38
+ // Health State
39
+ healthState: string;
40
+ triggerReason: string;
41
+ consecutiveFailures: number;
42
+ consecutiveSuccesses: number;
43
+ totalFailuresSinceLastHealthy: number;
44
+ outageStartTime?: Date; // UTC time the current outage began; absent when healthy
45
+ // Cycle Metrics
46
+ pollCycle: number;
47
+ lastPollMs: number;
48
+ activeIncidents: number;
49
+ activeUnits: number;
50
+ // Data Flow Deltas
51
+ incidentsDelta: number;
52
+ unitStatusDelta: number;
53
+ // Queue Depths
54
+ queueDepths: QueueDepthsType;
55
+ // Auth Token Health (Bearer auth only; null otherwise)
56
+ tokenAgeSecs?: number;
57
+ tokenExpiresAt?: Date;
58
+ // TC Send Health
59
+ tCSend: TCSendHealthType;
60
+ // Write-back Health
61
+ writeBack: WriteBackHealthType;
62
+ // Sync Health
63
+ sync: SyncHealthType;
64
+ // Debug & Config
65
+ debugPushActive: boolean;
66
+ lastConfigSave?: Date;
67
+ lastConfigSaveFile: string;
68
+ // Error & Version
69
+ lastError: string;
70
+ interfaceVersion: string;
71
+ cadVersion: string;
72
+ unifiedVersion: string;
73
+ // System (managed by Mongoose timestamps)
74
+ createdAt: Date;
75
+ updatedAt: Date;
76
+ }
77
+
78
+ export interface StatusBeaconTransitionType {
79
+ _id: Types.ObjectId;
80
+ departmentId: string;
81
+ integration: string;
82
+ triggerReason: string;
83
+ healthState: string;
84
+ consecutiveFailures: number;
85
+ totalFailuresSinceLastHealthy: number;
86
+ lastError: string;
87
+ // System (managed by Mongoose timestamps)
88
+ createdAt: Date;
89
+ updatedAt: Date;
90
+ }