tabletcommand-backend-models 7.4.89 → 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 (42) 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/schema/webhook.js +9 -1
  5. package/build/models/schema/webhook.js.map +1 -1
  6. package/build/models/status-beacon-transition.js +51 -0
  7. package/build/models/status-beacon-transition.js.map +1 -0
  8. package/build/models/status-beacon.js +174 -0
  9. package/build/models/status-beacon.js.map +1 -0
  10. package/build/test/0index.js +2 -0
  11. package/build/test/0index.js.map +1 -1
  12. package/build/test/mock.js +64 -0
  13. package/build/test/mock.js.map +1 -1
  14. package/build/test/status-beacon.js +112 -0
  15. package/build/test/status-beacon.js.map +1 -0
  16. package/build/types/status-beacon.js +3 -0
  17. package/build/types/status-beacon.js.map +1 -0
  18. package/definitions/index.d.ts +12 -0
  19. package/definitions/index.d.ts.map +1 -1
  20. package/definitions/models/schema/webhook.d.ts.map +1 -1
  21. package/definitions/models/status-beacon-transition.d.ts +13 -0
  22. package/definitions/models/status-beacon-transition.d.ts.map +1 -0
  23. package/definitions/models/status-beacon.d.ts +13 -0
  24. package/definitions/models/status-beacon.d.ts.map +1 -0
  25. package/definitions/test/mock.d.ts +62 -0
  26. package/definitions/test/mock.d.ts.map +1 -1
  27. package/definitions/test/status-beacon.d.ts +2 -0
  28. package/definitions/test/status-beacon.d.ts.map +1 -0
  29. package/definitions/types/department.d.ts +2 -0
  30. package/definitions/types/department.d.ts.map +1 -1
  31. package/definitions/types/status-beacon.d.ts +72 -0
  32. package/definitions/types/status-beacon.d.ts.map +1 -0
  33. package/package.json +1 -1
  34. package/src/index.ts +4 -0
  35. package/src/models/schema/webhook.ts +9 -1
  36. package/src/models/status-beacon-transition.ts +60 -0
  37. package/src/models/status-beacon.ts +193 -0
  38. package/src/test/0index.ts +2 -0
  39. package/src/test/mock.ts +66 -0
  40. package/src/test/status-beacon.ts +121 -0
  41. package/src/types/department.ts +2 -0
  42. package/src/types/status-beacon.ts +90 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabletcommand-backend-models",
3
- "version": "7.4.89",
3
+ "version": "7.4.91",
4
4
  "description": "Tablet Command Backend Models",
5
5
  "scripts": {
6
6
  "lint": "eslint src",
package/src/index.ts CHANGED
@@ -79,6 +79,8 @@ async function wireModels(mongoose: MongooseModule) {
79
79
  SAML: await getModel(import("./models/saml")),
80
80
  SMSLog: await getModel(import("./models/sms-log")),
81
81
  SMTPUnhandled: await getModel(import("./models/smtp-unhandled")),
82
+ StatusBeacon: await getModel(import("./models/status-beacon")),
83
+ StatusBeaconTransition: await getModel(import("./models/status-beacon-transition")),
82
84
  Template: await getModel(import("./models/template")),
83
85
  User: await getModel(import("./models/user")),
84
86
  UserDevice: await getModel(import("./models/user-device")),
@@ -141,6 +143,8 @@ export { Session, SessionModel } from "./models/session";
141
143
  export { SAML, SAMLModel } from "./models/saml";
142
144
  export { SMSLog, SMSLogModel } from "./models/sms-log";
143
145
  export { SMTPUnhandled, SMTPUnhandledModel } from "./models/smtp-unhandled";
146
+ export { StatusBeacon, StatusBeaconModel } from "./models/status-beacon";
147
+ export { StatusBeaconTransition, StatusBeaconTransitionModel } from "./models/status-beacon-transition";
144
148
  export { Template, TemplateModel } from "./models/template";
145
149
  export { User, UserModel } from "./models/user";
146
150
  export { UserDevice, UserDeviceModel } from "./models/user-device";
@@ -122,7 +122,15 @@ export function WebhookProcessSchema(mongoose: MongooseModule) {
122
122
  locationUrl: {
123
123
  type: String,
124
124
  default: "",
125
- }
125
+ },
126
+ usePartnerUrl: {
127
+ type: Boolean,
128
+ default: false
129
+ },
130
+ partnerId: {
131
+ type: String,
132
+ default: ""
133
+ },
126
134
  }, {
127
135
  _id: false,
128
136
  id: false,
@@ -0,0 +1,60 @@
1
+ import {
2
+ MongooseModule,
3
+ } from "../helpers";
4
+ import { Model } from "mongoose";
5
+ import { StatusBeaconTransitionType } from "../types/status-beacon";
6
+
7
+ export interface StatusBeaconTransition extends StatusBeaconTransitionType { }
8
+
9
+ export default async function StatusBeaconTransitionModule(mongoose: MongooseModule) {
10
+ const { Schema } = mongoose;
11
+
12
+ const modelSchema = new Schema<StatusBeaconTransitionType>({
13
+ _id: {
14
+ type: Schema.Types.ObjectId,
15
+ auto: true,
16
+ },
17
+ departmentId: {
18
+ type: String,
19
+ default: "",
20
+ required: true,
21
+ },
22
+ integration: {
23
+ type: String,
24
+ default: "",
25
+ required: true,
26
+ },
27
+ triggerReason: {
28
+ type: String,
29
+ default: "",
30
+ },
31
+ healthState: {
32
+ type: String,
33
+ default: "",
34
+ },
35
+ consecutiveFailures: {
36
+ type: Number,
37
+ default: 0,
38
+ },
39
+ totalFailuresSinceLastHealthy: {
40
+ type: Number,
41
+ default: 0,
42
+ },
43
+ lastError: {
44
+ type: String,
45
+ default: "",
46
+ },
47
+ }, {
48
+ autoIndex: false,
49
+ timestamps: true,
50
+ });
51
+
52
+ modelSchema.index({ departmentId: 1, integration: 1, createdAt: -1 }, {
53
+ name: "departmentId_1_integration_1_createdAt_-1",
54
+ });
55
+ modelSchema.index({ createdAt: 1 }, { name: "createdAt_1" });
56
+
57
+ return mongoose.model<StatusBeaconTransition>("StatusBeaconTransition", modelSchema, "massive_status_beacon_transition", { overwriteModels: true });
58
+ }
59
+
60
+ export interface StatusBeaconTransitionModel extends Model<StatusBeaconTransition> { }
@@ -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
+ });
@@ -35,6 +35,8 @@ export interface WSFeedConnectionLocationOptionsType extends WSFeedConnectionOpt
35
35
  export interface WebhookProcessLocationConfigType {
36
36
  enabled: boolean,
37
37
  locationUrl: string,
38
+ usePartnerUrl: boolean,
39
+ partnerId: string,
38
40
  }
39
41
 
40
42
  export interface WebhookProcessCommentConfigType {
@@ -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
+ }