tabletcommand-backend-models 7.4.105 → 7.4.107
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.
- package/build/constants.js +1 -0
- package/build/constants.js.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/models/department.js +28 -1
- package/build/models/department.js.map +1 -1
- package/build/models/global-settings.js +11 -0
- package/build/models/global-settings.js.map +1 -1
- package/build/models/location.js +14 -0
- package/build/models/location.js.map +1 -1
- package/build/models/tracker-responder.js +96 -0
- package/build/models/tracker-responder.js.map +1 -0
- package/build/models/watchduty-event.js +124 -0
- package/build/models/watchduty-event.js.map +1 -0
- package/build/test/0index.js +1 -0
- package/build/test/0index.js.map +1 -1
- package/build/test/department.js +2 -0
- package/build/test/department.js.map +1 -1
- package/build/test/global-settings.js +4 -1
- package/build/test/global-settings.js.map +1 -1
- package/build/test/location.js +20 -0
- package/build/test/location.js.map +1 -1
- package/build/test/mock.js +28 -0
- package/build/test/mock.js.map +1 -1
- package/build/test/tracker-responder.js +136 -0
- package/build/test/tracker-responder.js.map +1 -0
- package/build/test/watchduty-event.js +153 -0
- package/build/test/watchduty-event.js.map +1 -0
- package/build/types/tracker-responder.js +3 -0
- package/build/types/tracker-responder.js.map +1 -0
- package/build/types/watchduty-event.js +3 -0
- package/build/types/watchduty-event.js.map +1 -0
- package/cspell.json +1 -0
- package/definitions/constants.d.ts +1 -0
- package/definitions/constants.d.ts.map +1 -1
- package/definitions/index.d.ts +12 -0
- package/definitions/index.d.ts.map +1 -1
- package/definitions/models/department.d.ts +3 -0
- package/definitions/models/department.d.ts.map +1 -1
- package/definitions/models/global-settings.d.ts.map +1 -1
- package/definitions/models/location.d.ts.map +1 -1
- package/definitions/models/tracker-responder.d.ts +13 -0
- package/definitions/models/tracker-responder.d.ts.map +1 -0
- package/definitions/models/watchduty-event.d.ts +13 -0
- package/definitions/models/watchduty-event.d.ts.map +1 -0
- package/definitions/test/mock.d.ts +25 -0
- package/definitions/test/mock.d.ts.map +1 -1
- package/definitions/test/tracker-responder.d.ts +2 -0
- package/definitions/test/tracker-responder.d.ts.map +1 -0
- package/definitions/test/watchduty-event.d.ts +2 -0
- package/definitions/test/watchduty-event.d.ts.map +1 -0
- package/definitions/types/department.d.ts +9 -0
- package/definitions/types/department.d.ts.map +1 -1
- package/definitions/types/global-settings.d.ts +4 -0
- package/definitions/types/global-settings.d.ts.map +1 -1
- package/definitions/types/location.d.ts +1 -0
- package/definitions/types/location.d.ts.map +1 -1
- package/definitions/types/tracker-responder.d.ts +20 -0
- package/definitions/types/tracker-responder.d.ts.map +1 -0
- package/definitions/types/watchduty-event.d.ts +29 -0
- package/definitions/types/watchduty-event.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/index.ts +4 -0
- package/src/models/department.ts +31 -0
- package/src/models/global-settings.ts +13 -0
- package/src/models/location.ts +15 -0
- package/src/models/tracker-responder.ts +107 -0
- package/src/models/watchduty-event.ts +135 -0
- package/src/test/0index.ts +1 -0
- package/src/test/department.ts +2 -0
- package/src/test/global-settings.ts +4 -0
- package/src/test/location.ts +22 -0
- package/src/test/mock.ts +29 -0
- package/src/test/tracker-responder.ts +144 -0
- package/src/test/watchduty-event.ts +163 -0
- package/src/types/department.ts +11 -0
- package/src/types/global-settings.ts +8 -0
- package/src/types/location.ts +1 -0
- package/src/types/tracker-responder.ts +28 -0
- package/src/types/watchduty-event.ts +36 -0
package/src/test/mock.ts
CHANGED
|
@@ -996,6 +996,34 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
|
|
|
996
996
|
expireAt: new Date("2025-12-12T01:02:03.000Z"),
|
|
997
997
|
referer: "tabletcommand.com",
|
|
998
998
|
},
|
|
999
|
+
watchDutySync: {
|
|
1000
|
+
lastModifiedAt: new Date("2025-11-11T04:05:06.000Z"),
|
|
1001
|
+
},
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
const watchDutyEvent = {
|
|
1005
|
+
watchDutyId: 123456,
|
|
1006
|
+
name: "Line Fire",
|
|
1007
|
+
description: "Vegetation fire off Highway 38",
|
|
1008
|
+
geoEventType: "wildfire",
|
|
1009
|
+
locationGeoJSON: {
|
|
1010
|
+
type: "Point",
|
|
1011
|
+
coordinates: [-117.1234, 34.1234],
|
|
1012
|
+
},
|
|
1013
|
+
address: "Highway 38, Angelus Oaks, CA",
|
|
1014
|
+
isPrescribed: false,
|
|
1015
|
+
areaSquareMeters: 5060594,
|
|
1016
|
+
containment: 42,
|
|
1017
|
+
evacuationOrders: "Zone A, Zone B",
|
|
1018
|
+
evacuationWarnings: "Zone C",
|
|
1019
|
+
evacuationAdvisories: "Zone D",
|
|
1020
|
+
evacuationShelterInPlace: "Zone E",
|
|
1021
|
+
regions: [
|
|
1022
|
+
{ name: "San Bernardino County", state: "CA" },
|
|
1023
|
+
],
|
|
1024
|
+
active: true,
|
|
1025
|
+
watchDutyCreatedAt: new Date("2025-08-01T10:00:00.000Z"),
|
|
1026
|
+
watchDutyModifiedAt: new Date("2025-08-02T11:00:00.000Z"),
|
|
999
1027
|
};
|
|
1000
1028
|
|
|
1001
1029
|
const audioStreamAuthentication = {
|
|
@@ -1682,5 +1710,6 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
|
|
|
1682
1710
|
userDevice,
|
|
1683
1711
|
userRegistration,
|
|
1684
1712
|
validationReport,
|
|
1713
|
+
watchDutyEvent,
|
|
1685
1714
|
};
|
|
1686
1715
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
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("TrackerResponder", function() {
|
|
8
|
+
let models: m.BackendModels, mongoose: m.MongooseModule;
|
|
9
|
+
beforeEach(async function() {
|
|
10
|
+
const c = await m.connect(config.url);
|
|
11
|
+
models = c.models;
|
|
12
|
+
mongoose = c.mongoose;
|
|
13
|
+
|
|
14
|
+
const mock = mockModule({
|
|
15
|
+
mongoose
|
|
16
|
+
});
|
|
17
|
+
await mock.beforeEach();
|
|
18
|
+
await models.TrackerResponder.deleteMany({});
|
|
19
|
+
});
|
|
20
|
+
afterEach(async function() {
|
|
21
|
+
await mongoose.disconnect();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("is saved with defaults", async function() {
|
|
25
|
+
const item = new models.TrackerResponder({
|
|
26
|
+
departmentId: "d1",
|
|
27
|
+
mac: "aa:bb:cc:dd:ee:01",
|
|
28
|
+
});
|
|
29
|
+
const sut = await item.save();
|
|
30
|
+
|
|
31
|
+
assert.isNotNull(sut._id);
|
|
32
|
+
assert.equal(sut.departmentId, "d1");
|
|
33
|
+
assert.equal(sut.mac, "AA:BB:CC:DD:EE:01");
|
|
34
|
+
assert.isTrue(sut.uuid !== "");
|
|
35
|
+
assert.equal(sut.trackerPersonnelId, "");
|
|
36
|
+
assert.equal(sut.personnelName, "");
|
|
37
|
+
assert.equal(sut.friendlyName, "");
|
|
38
|
+
assert.equal(sut.displayName, "");
|
|
39
|
+
assert.equal(sut.seatPosition, "");
|
|
40
|
+
assert.equal(sut.personnelUUID, "");
|
|
41
|
+
assert.equal(sut.assignedBy, "");
|
|
42
|
+
assert.isNull(sut.assignedAt);
|
|
43
|
+
assert.isTrue(sut.active);
|
|
44
|
+
assert.equal(sut.source, "");
|
|
45
|
+
assert.instanceOf(sut.createdAt, Date);
|
|
46
|
+
assert.instanceOf(sut.modified, Date);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("persists vendor and user-owned fields", async function() {
|
|
50
|
+
const item = new models.TrackerResponder({
|
|
51
|
+
departmentId: "d1",
|
|
52
|
+
mac: "AA:BB:CC:DD:EE:02",
|
|
53
|
+
trackerPersonnelId: "12345",
|
|
54
|
+
personnelName: "Fire Hose 1",
|
|
55
|
+
friendlyName: "FH-1",
|
|
56
|
+
displayName: "Engine 1 Nozzle",
|
|
57
|
+
seatPosition: "Fire Hose 1",
|
|
58
|
+
personnelUUID: "pk-uuid-1",
|
|
59
|
+
assignedBy: "user-1",
|
|
60
|
+
assignedAt: new Date("2026-07-23T00:00:00.000Z"),
|
|
61
|
+
source: "TXRX",
|
|
62
|
+
});
|
|
63
|
+
const sut = await item.save();
|
|
64
|
+
|
|
65
|
+
assert.equal(sut.trackerPersonnelId, "12345");
|
|
66
|
+
assert.equal(sut.personnelName, "Fire Hose 1");
|
|
67
|
+
assert.equal(sut.friendlyName, "FH-1");
|
|
68
|
+
assert.equal(sut.displayName, "Engine 1 Nozzle");
|
|
69
|
+
assert.equal(sut.seatPosition, "Fire Hose 1");
|
|
70
|
+
assert.equal(sut.personnelUUID, "pk-uuid-1");
|
|
71
|
+
assert.equal(sut.assignedBy, "user-1");
|
|
72
|
+
assert.equal(sut.assignedAt?.toISOString(), "2026-07-23T00:00:00.000Z");
|
|
73
|
+
assert.equal(sut.source, "TXRX");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("enforces unique (departmentId, mac)", async function() {
|
|
77
|
+
await models.TrackerResponder.createIndexes();
|
|
78
|
+
|
|
79
|
+
await new models.TrackerResponder({
|
|
80
|
+
departmentId: "d1",
|
|
81
|
+
mac: "AA:BB:CC:DD:EE:03",
|
|
82
|
+
}).save();
|
|
83
|
+
|
|
84
|
+
// Same MAC in another department is allowed
|
|
85
|
+
const other = await new models.TrackerResponder({
|
|
86
|
+
departmentId: "d2",
|
|
87
|
+
mac: "AA:BB:CC:DD:EE:03",
|
|
88
|
+
}).save();
|
|
89
|
+
assert.isNotNull(other._id);
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await new models.TrackerResponder({
|
|
93
|
+
departmentId: "d1",
|
|
94
|
+
mac: "aa:bb:cc:dd:ee:03",
|
|
95
|
+
}).save();
|
|
96
|
+
assert.isFalse(true, "Expecting duplicate (departmentId, mac) to fail.");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
assert.match((error as Error).message, /E11000/);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("enforces unique (departmentId, uuid)", async function() {
|
|
103
|
+
await models.TrackerResponder.createIndexes();
|
|
104
|
+
|
|
105
|
+
const first = await new models.TrackerResponder({
|
|
106
|
+
departmentId: "d1",
|
|
107
|
+
mac: "AA:BB:CC:DD:EE:04",
|
|
108
|
+
}).save();
|
|
109
|
+
|
|
110
|
+
// Same uuid in another department is allowed
|
|
111
|
+
const other = await new models.TrackerResponder({
|
|
112
|
+
departmentId: "d2",
|
|
113
|
+
mac: "AA:BB:CC:DD:EE:04",
|
|
114
|
+
uuid: first.uuid,
|
|
115
|
+
}).save();
|
|
116
|
+
assert.isNotNull(other._id);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await new models.TrackerResponder({
|
|
120
|
+
departmentId: "d1",
|
|
121
|
+
mac: "AA:BB:CC:DD:EE:05",
|
|
122
|
+
uuid: first.uuid,
|
|
123
|
+
}).save();
|
|
124
|
+
assert.isFalse(true, "Expecting duplicate (departmentId, uuid) to fail.");
|
|
125
|
+
} catch (error) {
|
|
126
|
+
assert.match((error as Error).message, /E11000/);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("rejects an empty or null uuid", async function() {
|
|
131
|
+
for (const uuid of ["", null]) {
|
|
132
|
+
try {
|
|
133
|
+
await new models.TrackerResponder({
|
|
134
|
+
departmentId: "d1",
|
|
135
|
+
mac: "AA:BB:CC:DD:EE:06",
|
|
136
|
+
uuid,
|
|
137
|
+
}).save();
|
|
138
|
+
assert.isFalse(true, `Expecting uuid ${JSON.stringify(uuid)} to fail validation.`);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
assert.match((error as Error).message, /uuid.*required/i);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
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("WatchDutyEvent", function() {
|
|
8
|
+
let models: m.BackendModels, mongoose: m.MongooseModule;
|
|
9
|
+
let testItem: ReturnType<typeof mockModule>["watchDutyEvent"];
|
|
10
|
+
|
|
11
|
+
beforeEach(async function() {
|
|
12
|
+
const c = await m.connect(config.url);
|
|
13
|
+
models = c.models;
|
|
14
|
+
mongoose = c.mongoose;
|
|
15
|
+
testItem = mockModule({ mongoose }).watchDutyEvent;
|
|
16
|
+
await models.WatchDutyEvent.syncIndexes();
|
|
17
|
+
});
|
|
18
|
+
afterEach(async function() {
|
|
19
|
+
await models.WatchDutyEvent.deleteMany({});
|
|
20
|
+
await mongoose.disconnect();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("is saved and reads back every field", async function() {
|
|
24
|
+
const sut = await new models.WatchDutyEvent(testItem).save();
|
|
25
|
+
|
|
26
|
+
assert.isNotNull(sut._id);
|
|
27
|
+
assert.equal(sut.watchDutyId, testItem.watchDutyId);
|
|
28
|
+
assert.equal(sut.name, testItem.name);
|
|
29
|
+
assert.equal(sut.description, testItem.description);
|
|
30
|
+
assert.equal(sut.geoEventType, testItem.geoEventType);
|
|
31
|
+
assert.equal(sut.locationGeoJSON?.type, "Point");
|
|
32
|
+
assert.equal(sut.locationGeoJSON?.coordinates[0], testItem.locationGeoJSON.coordinates[0]);
|
|
33
|
+
assert.equal(sut.locationGeoJSON?.coordinates[1], testItem.locationGeoJSON.coordinates[1]);
|
|
34
|
+
assert.equal(sut.address, testItem.address);
|
|
35
|
+
assert.isFalse(sut.isPrescribed);
|
|
36
|
+
assert.equal(sut.areaSquareMeters, testItem.areaSquareMeters);
|
|
37
|
+
assert.equal(sut.containment, testItem.containment);
|
|
38
|
+
assert.equal(sut.evacuationOrders, testItem.evacuationOrders);
|
|
39
|
+
assert.equal(sut.evacuationWarnings, testItem.evacuationWarnings);
|
|
40
|
+
assert.equal(sut.evacuationAdvisories, testItem.evacuationAdvisories);
|
|
41
|
+
assert.equal(sut.evacuationShelterInPlace, testItem.evacuationShelterInPlace);
|
|
42
|
+
assert.equal(sut.regions.length, 1);
|
|
43
|
+
assert.equal(sut.regions[0]?.name, testItem.regions[0]?.name);
|
|
44
|
+
assert.equal(sut.regions[0]?.state, testItem.regions[0]?.state);
|
|
45
|
+
assert.isTrue(sut.active);
|
|
46
|
+
assert.equal(sut.watchDutyCreatedAt.toISOString(), testItem.watchDutyCreatedAt.toISOString());
|
|
47
|
+
assert.equal(sut.watchDutyModifiedAt.toISOString(), testItem.watchDutyModifiedAt.toISOString());
|
|
48
|
+
assert.instanceOf(sut.created, Date);
|
|
49
|
+
assert.instanceOf(sut.modified, Date);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("defaults optional fields", async function() {
|
|
53
|
+
const sut = await new models.WatchDutyEvent({
|
|
54
|
+
watchDutyId: 654321,
|
|
55
|
+
watchDutyCreatedAt: new Date(),
|
|
56
|
+
watchDutyModifiedAt: new Date(),
|
|
57
|
+
}).save();
|
|
58
|
+
|
|
59
|
+
assert.equal(sut.name, "");
|
|
60
|
+
assert.equal(sut.description, "");
|
|
61
|
+
assert.equal(sut.geoEventType, "");
|
|
62
|
+
assert.isNull(sut.locationGeoJSON);
|
|
63
|
+
assert.equal(sut.address, "");
|
|
64
|
+
assert.isFalse(sut.isPrescribed);
|
|
65
|
+
assert.isNull(sut.areaSquareMeters);
|
|
66
|
+
assert.isNull(sut.containment);
|
|
67
|
+
assert.equal(sut.evacuationOrders, "");
|
|
68
|
+
assert.equal(sut.evacuationWarnings, "");
|
|
69
|
+
assert.equal(sut.evacuationAdvisories, "");
|
|
70
|
+
assert.equal(sut.evacuationShelterInPlace, "");
|
|
71
|
+
assert.deepEqual(sut.regions, []);
|
|
72
|
+
assert.isTrue(sut.active);
|
|
73
|
+
assert.instanceOf(sut.created, Date);
|
|
74
|
+
assert.instanceOf(sut.modified, Date);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// timestamps defaults createdAt to true, which would store an undeclared createdAt field.
|
|
78
|
+
it("stores no timestamp field outside the schema", async function() {
|
|
79
|
+
await new models.WatchDutyEvent({
|
|
80
|
+
watchDutyId: 333,
|
|
81
|
+
watchDutyCreatedAt: new Date(),
|
|
82
|
+
watchDutyModifiedAt: new Date(),
|
|
83
|
+
}).save();
|
|
84
|
+
|
|
85
|
+
const raw = await models.WatchDutyEvent.collection.findOne({ watchDutyId: 333 });
|
|
86
|
+
assert.isNotNull(raw);
|
|
87
|
+
assert.notProperty(raw, "createdAt");
|
|
88
|
+
assert.notProperty(raw, "updatedAt");
|
|
89
|
+
assert.property(raw, "created");
|
|
90
|
+
assert.property(raw, "modified");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// The retention sweep deletes inactive events by `modified`, and the cron upserts through
|
|
94
|
+
// findOneAndUpdate, which skips the schema default. Only the timestamps option keeps the
|
|
95
|
+
// field current.
|
|
96
|
+
it("advances modified on findOneAndUpdate", async function() {
|
|
97
|
+
const staleDate = new Date("2020-01-02T03:04:05.000Z");
|
|
98
|
+
await new models.WatchDutyEvent({
|
|
99
|
+
watchDutyId: 222,
|
|
100
|
+
watchDutyCreatedAt: new Date(),
|
|
101
|
+
watchDutyModifiedAt: new Date(),
|
|
102
|
+
}).save();
|
|
103
|
+
await models.WatchDutyEvent.collection.updateOne({
|
|
104
|
+
watchDutyId: 222,
|
|
105
|
+
}, {
|
|
106
|
+
$set: {
|
|
107
|
+
modified: staleDate,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const updated = await models.WatchDutyEvent.findOneAndUpdate({
|
|
112
|
+
watchDutyId: 222,
|
|
113
|
+
}, {
|
|
114
|
+
$set: {
|
|
115
|
+
active: false,
|
|
116
|
+
},
|
|
117
|
+
}, {
|
|
118
|
+
new: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
assert.isNotNull(updated);
|
|
122
|
+
assert.isFalse(updated?.active);
|
|
123
|
+
assert.isAbove(Number(updated?.modified), Number(staleDate));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("enforces a unique watchDutyId index", async function() {
|
|
127
|
+
await new models.WatchDutyEvent({
|
|
128
|
+
watchDutyId: 111,
|
|
129
|
+
watchDutyCreatedAt: new Date(),
|
|
130
|
+
watchDutyModifiedAt: new Date(),
|
|
131
|
+
}).save();
|
|
132
|
+
|
|
133
|
+
let thrownError: unknown = null;
|
|
134
|
+
try {
|
|
135
|
+
await new models.WatchDutyEvent({
|
|
136
|
+
watchDutyId: 111,
|
|
137
|
+
watchDutyCreatedAt: new Date(),
|
|
138
|
+
watchDutyModifiedAt: new Date(),
|
|
139
|
+
}).save();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
thrownError = e;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
assert.isNotNull(thrownError, "expected duplicate watchDutyId to be rejected");
|
|
145
|
+
assert.equal((thrownError as { code?: number }).code, 11000);
|
|
146
|
+
assert.include(String((thrownError as Error).message), "E11000");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("requires watchDutyId", async function() {
|
|
150
|
+
let thrownError: unknown = null;
|
|
151
|
+
try {
|
|
152
|
+
await new models.WatchDutyEvent({
|
|
153
|
+
watchDutyCreatedAt: new Date(),
|
|
154
|
+
watchDutyModifiedAt: new Date(),
|
|
155
|
+
}).save();
|
|
156
|
+
} catch (e) {
|
|
157
|
+
thrownError = e;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
assert.isNotNull(thrownError, "expected missing watchDutyId to be rejected");
|
|
161
|
+
assert.include(String((thrownError as Error).message), "watchDutyId");
|
|
162
|
+
});
|
|
163
|
+
});
|
package/src/types/department.ts
CHANGED
|
@@ -205,6 +205,10 @@ export interface SomewearType {
|
|
|
205
205
|
enabled: boolean
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
export interface PersonnelWebhookType {
|
|
209
|
+
enabled: boolean
|
|
210
|
+
}
|
|
211
|
+
|
|
208
212
|
export interface SkytracConfigType {
|
|
209
213
|
enabled: boolean
|
|
210
214
|
licenseKey: string,
|
|
@@ -546,6 +550,11 @@ export type AccountConfigurationZonehaven = {
|
|
|
546
550
|
fadeZoomLevel: number,
|
|
547
551
|
};
|
|
548
552
|
|
|
553
|
+
export type AccountConfigurationWatchDuty = {
|
|
554
|
+
enabled: boolean,
|
|
555
|
+
visible: boolean,
|
|
556
|
+
};
|
|
557
|
+
|
|
549
558
|
export type ManagedIncidentPriorityItem = {
|
|
550
559
|
title: string,
|
|
551
560
|
color: ColorSchemaType,
|
|
@@ -667,6 +676,7 @@ export interface DepartmentType {
|
|
|
667
676
|
orientationMarkerColor: ColorSchemaType,
|
|
668
677
|
partialApiKey: string,
|
|
669
678
|
personnelStaffingEnabled: boolean,
|
|
679
|
+
personnelWebhook: PersonnelWebhookType,
|
|
670
680
|
pubNubV3: PubNubTokenSchemaType,
|
|
671
681
|
remoteLoggingEnabled: boolean,
|
|
672
682
|
reportNumberEnabled: boolean,
|
|
@@ -705,4 +715,5 @@ export interface DepartmentType {
|
|
|
705
715
|
webhook: WebhookConfigType,
|
|
706
716
|
wsFeed: WSFeedConfigType,
|
|
707
717
|
zonehaven: AccountConfigurationZonehaven,
|
|
718
|
+
watchDuty: AccountConfigurationWatchDuty,
|
|
708
719
|
}
|
|
@@ -12,10 +12,18 @@ export type ZonehavenTokenType = {
|
|
|
12
12
|
referer: string | null,
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
// Incremental pull cursor for the Watch Duty events cron. One Watch Duty account feeds every
|
|
16
|
+
// department, so the cursor is global. The cron holds it back when a fetch is incomplete, which
|
|
17
|
+
// is why it is stored and not derived from the events themselves.
|
|
18
|
+
export type WatchDutySyncType = {
|
|
19
|
+
lastModifiedAt: Date | null,
|
|
20
|
+
};
|
|
21
|
+
|
|
15
22
|
export interface GlobalSettingsType {
|
|
16
23
|
_id: Types.ObjectId,
|
|
17
24
|
name: "global",
|
|
18
25
|
arcgisApiKey: ArcGISKeyType,
|
|
19
26
|
zonehavenToken: ZonehavenTokenType,
|
|
27
|
+
watchDutySync: WatchDutySyncType,
|
|
20
28
|
updatedAt: Date,
|
|
21
29
|
}
|
package/src/types/location.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Types } from "mongoose";
|
|
2
|
+
|
|
3
|
+
export interface TrackerResponderType {
|
|
4
|
+
_id: Types.ObjectId,
|
|
5
|
+
departmentId: string,
|
|
6
|
+
// Transponder MAC address, normalized uppercase. Unique per department.
|
|
7
|
+
mac: string,
|
|
8
|
+
// Stable identity carried by Location.transponderUUID.
|
|
9
|
+
uuid: string,
|
|
10
|
+
// Vendor personnelId verbatim (not a PersonnelKnown reference).
|
|
11
|
+
trackerPersonnelId: string,
|
|
12
|
+
// Vendor-owned names, taken verbatim from the TXRX webhook body.
|
|
13
|
+
personnelName: string,
|
|
14
|
+
friendlyName: string,
|
|
15
|
+
// User-owned display name, editable on our end (eventually via iOS);
|
|
16
|
+
// the webhook never touches this.
|
|
17
|
+
displayName: string,
|
|
18
|
+
seatPosition: string,
|
|
19
|
+
// "" = unassigned, otherwise PersonnelKnown.uuid.
|
|
20
|
+
personnelUUID: string,
|
|
21
|
+
assignedBy: string,
|
|
22
|
+
// null while unassigned
|
|
23
|
+
assignedAt: Date | null,
|
|
24
|
+
active: boolean,
|
|
25
|
+
source: string,
|
|
26
|
+
createdAt: Date,
|
|
27
|
+
modified: Date,
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Types } from "mongoose";
|
|
2
|
+
import { GeoPoint } from "./geojson";
|
|
3
|
+
|
|
4
|
+
export interface WatchDutyRegionType {
|
|
5
|
+
name: string,
|
|
6
|
+
state: string,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface WatchDutyEventType {
|
|
10
|
+
_id: Types.ObjectId,
|
|
11
|
+
watchDutyId: number,
|
|
12
|
+
name: string,
|
|
13
|
+
description: string,
|
|
14
|
+
geoEventType: string,
|
|
15
|
+
// Null when Watch Duty reports no usable lat/lng. The event is still stored, it just cannot
|
|
16
|
+
// be drawn.
|
|
17
|
+
locationGeoJSON: GeoPoint | null,
|
|
18
|
+
address: string,
|
|
19
|
+
isPrescribed: boolean,
|
|
20
|
+
// Watch Duty reports the burned area in acres. It is converted on the way in.
|
|
21
|
+
areaSquareMeters: number | null,
|
|
22
|
+
// Percent of the fire perimeter contained, 0 to 100.
|
|
23
|
+
containment: number | null,
|
|
24
|
+
evacuationOrders: string,
|
|
25
|
+
evacuationWarnings: string,
|
|
26
|
+
evacuationAdvisories: string,
|
|
27
|
+
evacuationShelterInPlace: string,
|
|
28
|
+
regions: WatchDutyRegionType[],
|
|
29
|
+
active: boolean,
|
|
30
|
+
// Watch Duty's own timestamps. `created` and `modified` are ours, and mean the first and the
|
|
31
|
+
// last time we wrote the record.
|
|
32
|
+
watchDutyCreatedAt: Date,
|
|
33
|
+
watchDutyModifiedAt: Date,
|
|
34
|
+
created: Date,
|
|
35
|
+
modified: Date,
|
|
36
|
+
}
|