tabletcommand-backend-models 7.4.32 → 7.4.34

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 (45) hide show
  1. package/build/index.js +1 -0
  2. package/build/index.js.map +1 -1
  3. package/build/models/audio-stream-authentication.js +52 -0
  4. package/build/models/audio-stream-authentication.js.map +1 -0
  5. package/build/models/cad-vehicle-status.js +2 -1
  6. package/build/models/cad-vehicle-status.js.map +1 -1
  7. package/build/models/department.js +23 -1
  8. package/build/models/department.js.map +1 -1
  9. package/build/test/audio-stream-authentication.js +74 -0
  10. package/build/test/audio-stream-authentication.js.map +1 -0
  11. package/build/test/audio-stream.js +212 -0
  12. package/build/test/audio-stream.js.map +1 -0
  13. package/build/test/department.js +1 -0
  14. package/build/test/department.js.map +1 -1
  15. package/build/test/mock.js +10 -0
  16. package/build/test/mock.js.map +1 -1
  17. package/build/types/audio-stream-authentication.js +3 -0
  18. package/build/types/audio-stream-authentication.js.map +1 -0
  19. package/definitions/index.d.ts +6 -0
  20. package/definitions/index.d.ts.map +1 -1
  21. package/definitions/models/audio-stream-authentication.d.ts +13 -0
  22. package/definitions/models/audio-stream-authentication.d.ts.map +1 -0
  23. package/definitions/models/cad-vehicle-status.d.ts.map +1 -1
  24. package/definitions/models/department.d.ts.map +1 -1
  25. package/definitions/test/audio-stream-authentication.d.ts +2 -0
  26. package/definitions/test/audio-stream-authentication.d.ts.map +1 -0
  27. package/definitions/test/audio-stream.d.ts +2 -0
  28. package/definitions/test/audio-stream.d.ts.map +1 -0
  29. package/definitions/test/mock.d.ts +9 -0
  30. package/definitions/test/mock.d.ts.map +1 -1
  31. package/definitions/types/audio-stream-authentication.d.ts +9 -0
  32. package/definitions/types/audio-stream-authentication.d.ts.map +1 -0
  33. package/definitions/types/department.d.ts +6 -0
  34. package/definitions/types/department.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/index.ts +2 -0
  37. package/src/models/audio-stream-authentication.ts +63 -0
  38. package/src/models/cad-vehicle-status.ts +2 -1
  39. package/src/models/department.ts +25 -1
  40. package/src/test/audio-stream-authentication.ts +81 -0
  41. package/src/test/audio-stream.ts +230 -0
  42. package/src/test/department.ts +1 -0
  43. package/src/test/mock.ts +11 -0
  44. package/src/types/audio-stream-authentication.ts +9 -0
  45. package/src/types/department.ts +7 -0
@@ -0,0 +1,81 @@
1
+ import { assert } from "chai";
2
+ import "mocha";
3
+
4
+ import * as m from "../index";
5
+ import * as config from "./config";
6
+ import mockModule from "./mock";
7
+
8
+ describe("AudioStreamAuthentication", function() {
9
+ let models: m.BackendModels, mongoose: m.MongooseModule;
10
+ let testItem: Partial<m.AudioStreamAuthentication>;
11
+ beforeEach(async function() {
12
+ const c = await m.connect(config.url);
13
+ models = c.models;
14
+ mongoose = c.mongoose;
15
+ const mock = mockModule({
16
+ mongoose
17
+ });
18
+ testItem = mock.audioStreamAuthentication;
19
+ await mock.cleanup();
20
+ });
21
+
22
+ afterEach(async function() {
23
+ await mongoose.disconnect();
24
+ });
25
+
26
+ it("is saved", async function() {
27
+ assert.isObject(testItem);
28
+ const item = new models.AudioStreamAuthentication(testItem);
29
+ const sut = await item.save();
30
+ assert.isNotNull(sut._id);
31
+ assert.isNotNull(sut.id);
32
+ assert.equal(sut._id.toString(), testItem._id?.toString());
33
+
34
+ assert.equal(sut.name, "test-stream");
35
+ assert.equal(sut.rotateInterval, 3600);
36
+
37
+ assert.isObject(sut.secret);
38
+ assert.equal(sut.secret?.iv, testItem.secret?.iv);
39
+ assert.equal(sut.secret?.encryptedData, testItem.secret?.encryptedData);
40
+ });
41
+
42
+ it("enforces unique name constraint", async function() {
43
+ const item1 = new models.AudioStreamAuthentication(testItem);
44
+ await item1.save();
45
+
46
+ const item2 = new models.AudioStreamAuthentication({
47
+ name: "test-stream",
48
+ secret: {
49
+ iv: "different-iv",
50
+ encryptedData: "different-data"
51
+ },
52
+ rotateInterval: 7200,
53
+ });
54
+
55
+ try {
56
+ await item2.save();
57
+ assert.fail("Should have thrown duplicate key error");
58
+ } catch (error) {
59
+ assert.include(String((error as Error).message), "duplicate key");
60
+ }
61
+ });
62
+
63
+ it("allows different names", async function() {
64
+ const item1 = new models.AudioStreamAuthentication(testItem);
65
+ await item1.save();
66
+
67
+ const item2 = new models.AudioStreamAuthentication({
68
+ name: "different-stream",
69
+ secret: {
70
+ iv: "different-iv",
71
+ encryptedData: "different-data"
72
+ },
73
+ rotateInterval: 7200,
74
+ });
75
+
76
+ const sut = await item2.save();
77
+ assert.isNotNull(sut._id);
78
+ assert.equal(sut.name, "different-stream");
79
+ assert.equal(sut.rotateInterval, 7200);
80
+ });
81
+ });
@@ -0,0 +1,230 @@
1
+ import { assert } from "chai";
2
+ import "mocha";
3
+
4
+ import * as m from "../index";
5
+ import * as config from "./config";
6
+ import mockModule from "./mock";
7
+
8
+ describe("AudioStream", function() {
9
+ let models: m.BackendModels, mongoose: m.MongooseModule;
10
+ let authRecord: m.AudioStreamAuthentication;
11
+
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
+ await mock.cleanup();
20
+
21
+ // Create an AudioStreamAuthentication record to reference
22
+ const authItem = new models.AudioStreamAuthentication({
23
+ name: "test-auth-record",
24
+ secret: {
25
+ iv: "test-iv",
26
+ encryptedData: "test-encrypted-data"
27
+ },
28
+ rotateInterval: 3600,
29
+ });
30
+ authRecord = await authItem.save();
31
+ });
32
+
33
+ afterEach(async function() {
34
+ await mongoose.disconnect();
35
+ });
36
+
37
+ it("saves Department with AudioStream authentication", async function() {
38
+ const department = new models.Department({
39
+ department: "Test Fire Department",
40
+ apikey: "test-api-key-123",
41
+ audioConfiguration: [
42
+ {
43
+ group: "Fire Dispatch",
44
+ order: 1,
45
+ streams: [
46
+ {
47
+ description: "Main Dispatch Channel",
48
+ channel: "CH1",
49
+ url: "https://example.com/stream1",
50
+ order: 1,
51
+ authentication: {
52
+ authId: authRecord._id,
53
+ app: "test-app",
54
+ streamKey: "stream-key-123",
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ });
61
+
62
+ const saved = await department.save();
63
+
64
+ assert.isNotNull(saved._id);
65
+ assert.isArray(saved.audioConfiguration);
66
+ assert.equal(saved.audioConfiguration.length, 1);
67
+
68
+ const group = saved.audioConfiguration[0];
69
+ if (!group) {
70
+ assert.fail("Group should be defined");
71
+ return;
72
+ }
73
+ assert.equal(group.group, "Fire Dispatch");
74
+ assert.equal(group.order, 1);
75
+ assert.equal(group.streams.length, 1);
76
+
77
+ const stream = group.streams[0];
78
+ if (!stream) {
79
+ assert.fail("Stream should be defined");
80
+ return;
81
+ }
82
+ assert.equal(stream.description, "Main Dispatch Channel");
83
+ assert.equal(stream.channel, "CH1");
84
+ assert.equal(stream.url, "https://example.com/stream1");
85
+ assert.equal(stream.order, 1);
86
+
87
+ // Verify authentication
88
+ assert.isObject(stream.authentication);
89
+ if (!stream.authentication) {
90
+ assert.fail("Authentication should be defined");
91
+ return;
92
+ }
93
+ assert.equal(stream.authentication.authId.toString(), authRecord._id.toString());
94
+ assert.equal(stream.authentication.app, "test-app");
95
+ assert.equal(stream.authentication.streamKey, "stream-key-123");
96
+ });
97
+
98
+ it("saves Department with AudioStream without authentication", async function() {
99
+ const department = new models.Department({
100
+ department: "Test Fire Department 2",
101
+ apikey: "test-api-key-456",
102
+ audioConfiguration: [
103
+ {
104
+ group: "Police Dispatch",
105
+ order: 1,
106
+ streams: [
107
+ {
108
+ description: "Public Channel",
109
+ channel: "CH2",
110
+ url: "https://example.com/stream2",
111
+ order: 1,
112
+ }
113
+ ]
114
+ }
115
+ ]
116
+ });
117
+
118
+ const saved = await department.save();
119
+
120
+ assert.isNotNull(saved._id);
121
+ assert.isArray(saved.audioConfiguration);
122
+ assert.equal(saved.audioConfiguration.length, 1);
123
+
124
+ const group = saved.audioConfiguration[0];
125
+ if (!group) {
126
+ assert.fail("Group should be defined");
127
+ return;
128
+ }
129
+ const stream = group.streams[0];
130
+ if (!stream) {
131
+ assert.fail("Stream should be defined");
132
+ return;
133
+ }
134
+ assert.equal(stream.description, "Public Channel");
135
+ assert.isUndefined(stream.authentication);
136
+ });
137
+
138
+ it("saves Department with mixed AudioStreams (with and without auth)", async function() {
139
+ const department = new models.Department({
140
+ department: "Test Mixed Department",
141
+ apikey: "test-api-key-789",
142
+ audioConfiguration: [
143
+ {
144
+ group: "Dispatch Channels",
145
+ order: 1,
146
+ streams: [
147
+ {
148
+ description: "Secure Channel",
149
+ channel: "CH1",
150
+ url: "https://example.com/secure",
151
+ order: 1,
152
+ authentication: {
153
+ authId: authRecord._id,
154
+ app: "secure-app",
155
+ streamKey: "secure-key-456",
156
+ }
157
+ },
158
+ {
159
+ description: "Public Channel",
160
+ channel: "CH2",
161
+ url: "https://example.com/public",
162
+ order: 2,
163
+ }
164
+ ]
165
+ }
166
+ ]
167
+ });
168
+
169
+ const saved = await department.save();
170
+
171
+ const group = saved.audioConfiguration[0];
172
+ if (!group) {
173
+ assert.fail("Group should be defined");
174
+ return;
175
+ }
176
+ assert.equal(group.streams.length, 2);
177
+
178
+ const secureStream = group.streams[0];
179
+ if (!secureStream) {
180
+ assert.fail("Secure stream should be defined");
181
+ return;
182
+ }
183
+ assert.isObject(secureStream.authentication);
184
+ if (!secureStream.authentication) {
185
+ assert.fail("Authentication should be defined");
186
+ return;
187
+ }
188
+ assert.equal(secureStream.authentication.app, "secure-app");
189
+
190
+ const publicStream = group.streams[1];
191
+ if (!publicStream) {
192
+ assert.fail("Public stream should be defined");
193
+ return;
194
+ }
195
+ assert.isUndefined(publicStream.authentication);
196
+ });
197
+
198
+ it("validates required authentication fields when present", async function() {
199
+ const department = new models.Department({
200
+ department: "Test Invalid Department",
201
+ apikey: "test-api-key-invalid",
202
+ audioConfiguration: [
203
+ {
204
+ group: "Test Group",
205
+ order: 1,
206
+ streams: [
207
+ {
208
+ description: "Invalid Stream",
209
+ channel: "CH1",
210
+ url: "https://example.com/invalid",
211
+ order: 1,
212
+ authentication: {
213
+ authId: authRecord._id,
214
+ app: "test-app",
215
+ // Missing streamKey - should fail validation
216
+ } as unknown as { authId: m.AudioStreamAuthentication["_id"]; app: string; streamKey: string; }
217
+ }
218
+ ]
219
+ }
220
+ ]
221
+ });
222
+
223
+ try {
224
+ await department.save();
225
+ assert.fail("Should have thrown validation error");
226
+ } catch (error) {
227
+ assert.include(String((error as Error).message), "streamKey");
228
+ }
229
+ });
230
+ });
@@ -15,6 +15,7 @@ describe("Department", function() {
15
15
  mongoose
16
16
  });
17
17
  testItem = mock.department;
18
+ await mock.cleanup();
18
19
  });
19
20
 
20
21
  afterEach(async function() {
package/src/test/mock.ts CHANGED
@@ -939,6 +939,16 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
939
939
  },
940
940
  };
941
941
 
942
+ const audioStreamAuthentication = {
943
+ _id: new mongoose.Types.ObjectId(),
944
+ name: "test-stream",
945
+ secret: {
946
+ iv: "a1b2c3d4e5f6",
947
+ encryptedData: "encrypted-secret-data-here"
948
+ },
949
+ rotateInterval: 3600,
950
+ };
951
+
942
952
  const gstMapping = {
943
953
  _id: new mongoose.Types.ObjectId(),
944
954
  departmentId: "d123",
@@ -1475,6 +1485,7 @@ export default function mockModule(dependencies: { mongoose: Mongoose; }) {
1475
1485
  agency,
1476
1486
  arcGISGroup,
1477
1487
  assignment,
1488
+ audioStreamAuthentication,
1478
1489
  battalion,
1479
1490
  cadIncident,
1480
1491
  cadIncidentBlock,
@@ -0,0 +1,9 @@
1
+ import { Types } from "mongoose";
2
+ import { EncryptedDataType } from "./common";
3
+
4
+ export interface AudioStreamAuthenticationType {
5
+ _id: Types.ObjectId,
6
+ secret: EncryptedDataType,
7
+ rotateInterval: number,
8
+ name: string,
9
+ }
@@ -243,11 +243,18 @@ export interface WebDisclaimerType {
243
243
  enabled: boolean,
244
244
  }
245
245
 
246
+ export interface AudioStreamAuthType {
247
+ authId: Types.ObjectId,
248
+ app: string,
249
+ streamKey: string,
250
+ }
251
+
246
252
  export interface AudioStreamType {
247
253
  description: string,
248
254
  channel: string,
249
255
  url: string,
250
256
  order: number,
257
+ authentication?: AudioStreamAuthType,
251
258
  }
252
259
 
253
260
  export interface AudioStreamGroupType {