tabletcommand-backend-models 7.4.113 → 7.4.115

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 (40) hide show
  1. package/build/index.js +1 -0
  2. package/build/index.js.map +1 -1
  3. package/build/models/department.js +36 -1
  4. package/build/models/department.js.map +1 -1
  5. package/build/models/zonehaven-account.js +67 -0
  6. package/build/models/zonehaven-account.js.map +1 -0
  7. package/build/test/0index.js +1 -0
  8. package/build/test/0index.js.map +1 -1
  9. package/build/test/department.js +32 -0
  10. package/build/test/department.js.map +1 -1
  11. package/build/test/mock.js +16 -0
  12. package/build/test/mock.js.map +1 -1
  13. package/build/test/zonehaven-account.js +64 -0
  14. package/build/test/zonehaven-account.js.map +1 -0
  15. package/build/types/zonehaven-account.js +3 -0
  16. package/build/types/zonehaven-account.js.map +1 -0
  17. package/definitions/index.d.ts +8 -1
  18. package/definitions/index.d.ts.map +1 -1
  19. package/definitions/models/department.d.ts +3 -1
  20. package/definitions/models/department.d.ts.map +1 -1
  21. package/definitions/models/zonehaven-account.d.ts +13 -0
  22. package/definitions/models/zonehaven-account.d.ts.map +1 -0
  23. package/definitions/test/mock.d.ts +2 -0
  24. package/definitions/test/mock.d.ts.map +1 -1
  25. package/definitions/test/zonehaven-account.d.ts +2 -0
  26. package/definitions/test/zonehaven-account.d.ts.map +1 -0
  27. package/definitions/types/department.d.ts +9 -0
  28. package/definitions/types/department.d.ts.map +1 -1
  29. package/definitions/types/zonehaven-account.d.ts +15 -0
  30. package/definitions/types/zonehaven-account.d.ts.map +1 -0
  31. package/package.json +1 -1
  32. package/src/index.ts +8 -0
  33. package/src/models/department.ts +40 -0
  34. package/src/models/zonehaven-account.ts +78 -0
  35. package/src/test/0index.ts +1 -0
  36. package/src/test/department.ts +35 -0
  37. package/src/test/mock.ts +18 -0
  38. package/src/test/zonehaven-account.ts +70 -0
  39. package/src/types/department.ts +12 -0
  40. package/src/types/zonehaven-account.ts +15 -0
@@ -80,6 +80,8 @@ import {
80
80
  OneStepConfigurationType,
81
81
  DroneSenseConfigurationType,
82
82
  DronesConfigurationType,
83
+ DronesVideoType,
84
+ DronesVideoUrlsType,
83
85
  USFTConfigurationType,
84
86
  VehicleStatusWebhookConfigType,
85
87
  VehicleStatusWebhookConnectionType,
@@ -548,8 +550,14 @@ export const DroneSenseConfigurationDefault = {
548
550
  apiKey: "",
549
551
  };
550
552
 
553
+ export const DronesVideoDefault: DronesVideoType = {
554
+ sources: [],
555
+ urls: { web: "" },
556
+ };
557
+
551
558
  export const DronesConfigurationDefault = {
552
559
  enabled: false,
560
+ video: DronesVideoDefault,
553
561
  };
554
562
 
555
563
  export const OrientationMarkerColorDefault: ColorSchemaType = {
@@ -1534,11 +1542,39 @@ export default async function DepartmentModule(mongoose: MongooseModule) {
1534
1542
  id: false,
1535
1543
  });
1536
1544
 
1545
+ const DronesVideoUrls = new Schema<DronesVideoUrlsType>({
1546
+ web: {
1547
+ type: String,
1548
+ default: "",
1549
+ },
1550
+ }, {
1551
+ _id: false,
1552
+ id: false,
1553
+ });
1554
+
1555
+ const DronesVideo = new Schema<DronesVideoType>({
1556
+ sources: {
1557
+ type: [String],
1558
+ default: [],
1559
+ },
1560
+ urls: {
1561
+ type: DronesVideoUrls,
1562
+ default: () => ({ web: "" }),
1563
+ },
1564
+ }, {
1565
+ _id: false,
1566
+ id: false,
1567
+ });
1568
+
1537
1569
  const DronesConfiguration = new Schema<DronesConfigurationType>({
1538
1570
  enabled: {
1539
1571
  type: Boolean,
1540
1572
  default: false,
1541
1573
  },
1574
+ video: {
1575
+ type: DronesVideo,
1576
+ default: () => ({ sources: [], urls: { web: "" } }),
1577
+ },
1542
1578
  }, {
1543
1579
  _id: false,
1544
1580
  id: false,
@@ -2393,6 +2429,10 @@ export default async function DepartmentModule(mongoose: MongooseModule) {
2393
2429
  type: Number,
2394
2430
  default: 45000,
2395
2431
  },
2432
+ accountName: {
2433
+ type: String,
2434
+ default: "",
2435
+ },
2396
2436
  },
2397
2437
 
2398
2438
  watchDuty: {
@@ -0,0 +1,78 @@
1
+ import {
2
+ MongooseModule,
3
+ } from "../helpers";
4
+ import { Model } from "mongoose";
5
+ import { ZonehavenAccountType } from "../types/zonehaven-account";
6
+ import { EncryptedDataType } from "../types/common";
7
+
8
+ export interface ZonehavenAccount extends ZonehavenAccountType { }
9
+
10
+ export default async function ZonehavenAccountModule(mongoose: MongooseModule) {
11
+ const { Schema } = mongoose;
12
+
13
+ const EncryptedData = new Schema<EncryptedDataType>({
14
+ iv: {
15
+ type: String,
16
+ default: "",
17
+ },
18
+ encryptedData: {
19
+ type: String,
20
+ default: "",
21
+ },
22
+ }, {
23
+ _id: false,
24
+ id: false,
25
+ });
26
+
27
+ const modelSchema = new Schema<ZonehavenAccountType>({
28
+ _id: {
29
+ type: Schema.Types.ObjectId,
30
+ auto: true,
31
+ },
32
+ name: {
33
+ type: String,
34
+ required: true,
35
+ immutable: true,
36
+ },
37
+ url: {
38
+ type: String,
39
+ default: "",
40
+ },
41
+ username: {
42
+ type: String,
43
+ default: "",
44
+ },
45
+ password: {
46
+ type: EncryptedData,
47
+ default: null,
48
+ },
49
+ token: {
50
+ type: String,
51
+ default: null,
52
+ },
53
+ expireAt: {
54
+ type: Date,
55
+ },
56
+ referer: {
57
+ type: String,
58
+ default: null,
59
+ },
60
+ }, {
61
+ autoIndex: false,
62
+ timestamps: true,
63
+ toJSON: {
64
+ versionKey: false,
65
+ }
66
+ });
67
+
68
+ modelSchema.index({
69
+ name: 1,
70
+ }, {
71
+ name: "name_1_unique",
72
+ unique: true,
73
+ });
74
+
75
+ return mongoose.model<ZonehavenAccount>("ZonehavenAccount", modelSchema, "massive_zonehaven_account", { overwriteModels: true });
76
+ }
77
+
78
+ export interface ZonehavenAccountModel extends Model<ZonehavenAccount> { }
@@ -79,5 +79,6 @@ describe(" Models", function() {
79
79
  assert.isFunction(models.UserRegistration, "Missing UserRegistration");
80
80
  assert.isFunction(models.ValidationReport, "Missing ValidationReport");
81
81
  assert.isFunction(models.WatchDutyEvent, "Missing WatchDutyEvent");
82
+ assert.isFunction(models.ZonehavenAccount, "Missing ZonehavenAccount");
82
83
  });
83
84
  });
@@ -145,6 +145,41 @@ describe("Department", function () {
145
145
  assert.isFalse(sut.drones.enabled);
146
146
  });
147
147
 
148
+ it("defaults drones.video to no sources and an empty web url", async function () {
149
+ const item = new models.Department(testItem);
150
+ const sut = await item.save();
151
+ assert.deepEqual(sut.drones.video.sources, []);
152
+ assert.equal(sut.drones.video.urls.web, "");
153
+ });
154
+
155
+ it("saves drones.video sources and urls, with no subdocument _id", async function () {
156
+ const item = new models.Department({
157
+ ...testItem,
158
+ drones: {
159
+ enabled: true,
160
+ video: {
161
+ sources: ["Skydio"],
162
+ urls: { web: "https://video.example.com/player/{uuid}?token={token}" },
163
+ },
164
+ },
165
+ });
166
+ const sut = await item.save();
167
+ assert.deepEqual(sut.drones.video.sources, ["Skydio"]);
168
+ assert.equal(sut.drones.video.urls.web, "https://video.example.com/player/{uuid}?token={token}");
169
+ assert.isUndefined((sut.drones.video as unknown as { _id?: unknown })._id);
170
+ assert.isUndefined((sut.drones.video.urls as unknown as { _id?: unknown })._id);
171
+ });
172
+
173
+ it("loads drones.video with defaults for a document saved without drones", async function () {
174
+ const legacyItem = { ...testItem };
175
+ delete legacyItem.drones;
176
+ const item = new models.Department(legacyItem);
177
+ const sut = await item.save();
178
+ const loaded = await models.Department.findOne({ _id: sut._id });
179
+ assert.deepEqual(loaded?.drones.video.sources, []);
180
+ assert.equal(loaded?.drones.video.urls.web, "");
181
+ });
182
+
148
183
  it("saves samsara.tagIds and defaults it to an empty array", async function () {
149
184
  const item = new models.Department({
150
185
  ...testItem,
package/src/test/mock.ts CHANGED
@@ -18,6 +18,7 @@ import { IncidentNotified } from "../models/incident-notified";
18
18
  import { Location } from "../models/location";
19
19
  import { ManagedIncident } from "../models/managed-incident";
20
20
  import { SAML } from "../models/saml";
21
+ import { ZonehavenAccountType } from "../types/zonehaven-account";
21
22
 
22
23
  export default function mockModule(dependencies: { mongoose: Mongoose; }) {
23
24
  function shouldRun() {
@@ -1031,6 +1032,22 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
1031
1032
  watchDutyModifiedAt: new Date("2025-08-02T11:00:00.000Z"),
1032
1033
  };
1033
1034
 
1035
+ const zonehavenAccount: ZonehavenAccountType = {
1036
+ _id: new mongoose.Types.ObjectId(),
1037
+ name: "default",
1038
+ url: "https://agservergenasys.zonehaven.com/arcgis/rest/services/tc_data/FeatureServer/0",
1039
+ username: "zonehaven-user",
1040
+ password: {
1041
+ iv: "18f17",
1042
+ encryptedData: "dee54",
1043
+ },
1044
+ token: "test-zonehaven-token",
1045
+ expireAt: new Date("2025-12-12T01:02:03.000Z"),
1046
+ referer: "tabletcommand.com",
1047
+ createdAt: new Date("2025-11-10T20:01:01.111+0000"),
1048
+ updatedAt: new Date("2025-11-20T20:02:02.222+0000"),
1049
+ };
1050
+
1034
1051
  const dataSharingAgreement = {
1035
1052
  departmentId: "5195426cc4e016a988000965",
1036
1053
  departmentName: "Test Department",
@@ -1739,5 +1756,6 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
1739
1756
  userRegistration,
1740
1757
  validationReport,
1741
1758
  watchDutyEvent,
1759
+ zonehavenAccount,
1742
1760
  };
1743
1761
  }
@@ -0,0 +1,70 @@
1
+ import { assert } from "chai";
2
+ import { describe, it, beforeEach, afterEach } from "node:test";
3
+ import { isDate } from "lodash";
4
+
5
+ import * as m from "../index";
6
+ import * as config from "./config";
7
+ import mockModule from "./mock";
8
+
9
+ describe("ZonehavenAccount", function() {
10
+ let models: m.BackendModels, mongoose: m.MongooseModule;
11
+ let testItem: Partial<m.ZonehavenAccount>;
12
+ beforeEach(async function() {
13
+ const c = await m.connect(config.url);
14
+ models = c.models;
15
+ mongoose = c.mongoose;
16
+ const mock = mockModule({
17
+ mongoose
18
+ });
19
+ testItem = mock.zonehavenAccount;
20
+ await mock.beforeEach();
21
+ });
22
+
23
+ afterEach(async function() {
24
+ await mongoose.disconnect();
25
+ });
26
+
27
+ it("is saved", async function() {
28
+ assert.isObject(testItem);
29
+ const item = new models.ZonehavenAccount(testItem);
30
+ const sut = await item.save();
31
+ assert.isNotNull(sut._id);
32
+ assert.isNotNull(sut.id);
33
+ assert.equal(sut._id.toString(), testItem._id?.toString());
34
+
35
+ assert.equal(sut.name, "default");
36
+ assert.equal(sut.url, testItem.url);
37
+ assert.equal(sut.username, testItem.username);
38
+
39
+ assert.isObject(sut.password);
40
+ assert.equal(sut.password?.iv, testItem.password?.iv);
41
+ assert.equal(sut.password?.encryptedData, testItem.password?.encryptedData);
42
+
43
+ assert.equal(sut.token, testItem.token);
44
+ assert.isTrue(isDate(sut.expireAt));
45
+ assert.equal(sut.expireAt?.toISOString(), testItem.expireAt?.toISOString());
46
+ assert.equal(sut.referer, testItem.referer);
47
+ });
48
+
49
+ it("enforces a unique name", async function() {
50
+ const item = new models.ZonehavenAccount(testItem);
51
+ await item.save();
52
+
53
+ const duplicate = new models.ZonehavenAccount({ ...testItem, _id: new mongoose.Types.ObjectId() });
54
+ try {
55
+ await duplicate.save();
56
+ assert.fail("Should have thrown duplicate key error");
57
+ } catch (error) {
58
+ assert.include(String((error as Error).message), "duplicate key");
59
+ }
60
+ });
61
+
62
+ it("does not allow changing name after creation", async function() {
63
+ const item = new models.ZonehavenAccount(testItem);
64
+ const sut = await item.save();
65
+
66
+ sut.name = "demo";
67
+ const saved = await sut.save();
68
+ assert.equal(saved.name, "default");
69
+ });
70
+ });
@@ -359,8 +359,18 @@ export interface DroneSenseConfigurationType {
359
359
  apiKey: string,
360
360
  }
361
361
 
362
+ export interface DronesVideoUrlsType {
363
+ web: string,
364
+ }
365
+
366
+ export interface DronesVideoType {
367
+ sources: string[],
368
+ urls: DronesVideoUrlsType,
369
+ }
370
+
362
371
  export interface DronesConfigurationType {
363
372
  enabled: boolean,
373
+ video: DronesVideoType,
364
374
  }
365
375
 
366
376
  export interface FireMapperConfigurationType {
@@ -567,6 +577,8 @@ export type AccountConfigurationZonehaven = {
567
577
  visible: boolean,
568
578
  layerUrl: string,
569
579
  fadeZoomLevel: number,
580
+ // References ZonehavenAccount.name - empty string means unassigned/legacy fallback.
581
+ accountName: string,
570
582
  };
571
583
 
572
584
  export type AccountConfigurationWatchDuty = {
@@ -0,0 +1,15 @@
1
+ import { Types } from "mongoose";
2
+ import { EncryptedDataType } from "./common";
3
+
4
+ export interface ZonehavenAccountType {
5
+ _id: Types.ObjectId,
6
+ name: string, // "default", "demo" - unique, immutable once set
7
+ url: string,
8
+ username: string,
9
+ password: EncryptedDataType | null,
10
+ token: string | null,
11
+ expireAt: Date | null,
12
+ referer: string | null,
13
+ createdAt: Date,
14
+ updatedAt: Date,
15
+ }