@justair/justair-library 6.11.0 → 6.12.0-alpha.25e595a
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/dist/constants/alertLevels.d.ts +10 -0
- package/dist/constants/alertLevels.d.ts.map +1 -0
- package/dist/constants/index.d.ts +3 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/tests/constantsBarrel.test.d.ts +2 -0
- package/dist/constants/tests/constantsBarrel.test.d.ts.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/models/monitorEpisodes.d.ts +266 -0
- package/dist/models/monitorEpisodes.d.ts.map +1 -0
- package/dist/models/monitors.d.ts.map +1 -1
- package/dist/models/tests/monitorEpisodes.test.d.ts +2 -0
- package/dist/models/tests/monitorEpisodes.test.d.ts.map +1 -0
- package/dist/models/tests/monitorsLeftGoodAt.test.d.ts +2 -0
- package/dist/models/tests/monitorsLeftGoodAt.test.d.ts.map +1 -0
- package/dist/models/tests/userNotifications.test.d.ts +2 -0
- package/dist/models/tests/userNotifications.test.d.ts.map +1 -0
- package/dist/models/userNotifications.d.ts +330 -0
- package/dist/models/userNotifications.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/constants/alertLevels.js +24 -0
- package/src/constants/index.js +11 -0
- package/src/constants/tests/constantsBarrel.test.js +46 -0
- package/src/index.js +19 -0
- package/src/models/monitorEpisodes.js +80 -0
- package/src/models/monitors.js +6 -0
- package/src/models/userNotifications.js +51 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const ALERT_LEVEL_WEIGHTS: {
|
|
2
|
+
Good: number;
|
|
3
|
+
Moderate: number;
|
|
4
|
+
"Unhealthy for SG": number;
|
|
5
|
+
Unhealthy: number;
|
|
6
|
+
"Very Unhealthy": number;
|
|
7
|
+
Hazardous: number;
|
|
8
|
+
};
|
|
9
|
+
export function getAlertWeight(alertLevel: any): any;
|
|
10
|
+
//# sourceMappingURL=alertLevels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alertLevels.d.ts","sourceRoot":"","sources":["../../src/constants/alertLevels.js"],"names":[],"mappings":"AAQA;;;;;;;EAOE;AAOK,qDACsD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constantsBarrel.test.d.ts","sourceRoot":"","sources":["../../../src/constants/tests/constantsBarrel.test.js"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -75,8 +75,15 @@ import { SamplesAudit } from "./models/samples.js";
|
|
|
75
75
|
import { sampleParameterReferenceConcentrations } from "./models/samples.js";
|
|
76
76
|
import { passwordResetTokenSchema } from "./models/passwordResetToken.js";
|
|
77
77
|
import { PasswordResetToken } from "./models/passwordResetToken.js";
|
|
78
|
+
import { monitorEpisodesSchema } from "./models/monitorEpisodes.js";
|
|
79
|
+
import { MonitorEpisode } from "./models/monitorEpisodes.js";
|
|
80
|
+
import { userNotificationsSchema } from "./models/userNotifications.js";
|
|
81
|
+
import { UserNotification } from "./models/userNotifications.js";
|
|
82
|
+
import { NOTIFICATION_KINDS } from "./models/userNotifications.js";
|
|
78
83
|
import { PARAMETERS } from "./constants/pollutants.js";
|
|
79
84
|
import { HEAVY_METALS } from "./constants/pollutants.js";
|
|
85
|
+
import { ALERT_LEVEL_WEIGHTS } from "./constants/alertLevels.js";
|
|
86
|
+
import { getAlertWeight } from "./constants/alertLevels.js";
|
|
80
87
|
import { splitName } from "./utils/splitName.js";
|
|
81
|
-
export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, COOKIE_CONSENT_CATEGORIES, COOKIE_CONSENT_VALUES, validateCookieConsent, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, changeStreamCheckpointsSchema, ChangeStreamCheckpoints, sitesSchema, Sites, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, passwordResetTokenSchema, PasswordResetToken, PARAMETERS, HEAVY_METALS, splitName };
|
|
88
|
+
export { Database, adminSchema, Admin, configurationsSchema, Configurations, measurementsSchema, Measurements, monitorRequestsSchema, MonitorRequests, monitorsSchema, Monitors, organizationsSchema, Organizations, usersSchema, Users, COOKIE_CONSENT_CATEGORIES, COOKIE_CONSENT_VALUES, validateCookieConsent, eventsSchema, Events, lightMonitorSchema, LightMonitors, monitorSuppliersSchema, MonitorSuppliers, contextsSchema, Contexts, parametersSchema, Parameters, announcementSchema, Announcements, jobsSchema, Jobs, apiKeySchema, ApiKey, UsageMetrics, usageMetricsSchema, Audit, auditSchema, EventsAudit, eventsAuditSchema, MonitorAudit, monitorAuditSchema, parametersEnum, deploymentTypesEnum, AlertsAudit, Alerts, alertsSchema, alertsAuditSchema, Features, featuresSchema, dataCompletenessSchema, DataCompleteness, networkMetricsSchema, NetworkMetrics, rateOfChangeSchema, RateOfChange, changeStreamCheckpointsSchema, ChangeStreamCheckpoints, sitesSchema, Sites, sampleSitesSchema, SampleSites, sampleSiteAuditSchema, SampleSiteAudit, sampleParametersEnum, samplesSchema, Samples, samplesAuditSchema, SamplesAudit, sampleParameterReferenceConcentrations, passwordResetTokenSchema, PasswordResetToken, monitorEpisodesSchema, MonitorEpisode, userNotificationsSchema, UserNotification, NOTIFICATION_KINDS, PARAMETERS, HEAVY_METALS, ALERT_LEVEL_WEIGHTS, getAlertWeight, splitName };
|
|
82
89
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAsGA;;;iBAEC;yBAtCwB,oBAAoB;qBADxB,gBAAgB;4BAjEF,mBAAmB;sBAAnB,mBAAmB;qCAI/C,4BAA4B;+BAA5B,4BAA4B;mCAM5B,0BAA0B;6BAA1B,0BAA0B;sCAI1B,6BAA6B;gCAA7B,6BAA6B;+BAQ7B,sBAAsB;yBAAtB,sBAAsB;oCACsB,2BAA2B;8BAA3B,2BAA2B;4BAcvE,mBAAmB;sBAAnB,mBAAmB;0CAAnB,mBAAmB;sCAAnB,mBAAmB;sCAAnB,mBAAmB;6BAMnB,oBAAoB;uBAApB,oBAAoB;mCAnBuB,2BAA2B;8BAA3B,2BAA2B;uCAItE,8BAA8B;iCAA9B,8BAA8B;+BACI,sBAAsB;yBAAtB,sBAAsB;iCAClB,wBAAwB;2BAAxB,wBAAwB;mCAcnB,2BAA2B;8BAA3B,2BAA2B;2BAC5C,kBAAkB;qBAAlB,kBAAkB;6BACd,oBAAoB;uBAApB,oBAAoB;6BACR,0BAA0B;mCAA1B,0BAA0B;sBArCpE,0BAA0B;4BAA1B,0BAA0B;4BAiC1B,oBAAoB;kCAApB,oBAAoB;6BArBpB,sBAAsB;mCAAtB,sBAAsB;+BAAtB,sBAAsB;oCAAtB,sBAAsB;4BA+BtB,oBAAoB;uBAApB,oBAAoB;6BAApB,oBAAoB;kCAApB,oBAAoB;yBACc,sBAAsB;+BAAtB,sBAAsB;uCAIxD,8BAA8B;iCAA9B,8BAA8B;qCACgB,4BAA4B;+BAA5B,4BAA4B;mCAChC,0BAA0B;6BAA1B,0BAA0B;8CAIpE,qCAAqC;wCAArC,qCAAqC;4BAiBT,mBAAmB;sBAAnB,mBAAmB;kCAS/C,yBAAyB;4BAAzB,yBAAyB;sCAAzB,yBAAyB;gCAAzB,yBAAyB;qCAAzB,yBAAyB;8BASzB,qBAAqB;wBAArB,qBAAqB;mCAArB,qBAAqB;6BAArB,qBAAqB;uDAArB,qBAAqB;yCACiC,gCAAgC;mCAAhC,gCAAgC;sCA5BtF,6BAA6B;+BAA7B,6BAA6B;wCAK7B,+BAA+B;iCAA/B,+BAA+B;mCAA/B,+BAA+B;2BAVG,2BAA2B;6BAA3B,2BAA2B;oCAChB,4BAA4B;+BAA5B,4BAA4B;0BAUtD,sBAAsB"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
export const monitorEpisodesSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
2
|
+
timestamps: true;
|
|
3
|
+
}, {
|
|
4
|
+
createdAt: NativeDate;
|
|
5
|
+
updatedAt: NativeDate;
|
|
6
|
+
} & {
|
|
7
|
+
user: {
|
|
8
|
+
prototype?: mongoose.Types.ObjectId;
|
|
9
|
+
cacheHexString?: unknown;
|
|
10
|
+
generate?: {};
|
|
11
|
+
createFromTime?: {};
|
|
12
|
+
createFromHexString?: {};
|
|
13
|
+
createFromBase64?: {};
|
|
14
|
+
isValid?: {};
|
|
15
|
+
};
|
|
16
|
+
monitor: {
|
|
17
|
+
prototype?: mongoose.Types.ObjectId;
|
|
18
|
+
cacheHexString?: unknown;
|
|
19
|
+
generate?: {};
|
|
20
|
+
createFromTime?: {};
|
|
21
|
+
createFromHexString?: {};
|
|
22
|
+
createFromBase64?: {};
|
|
23
|
+
isValid?: {};
|
|
24
|
+
};
|
|
25
|
+
open: boolean;
|
|
26
|
+
startedAt: Date;
|
|
27
|
+
alertCount: number;
|
|
28
|
+
firstNotifiedAt?: Date;
|
|
29
|
+
endedAt?: Date;
|
|
30
|
+
peakStatus?: string;
|
|
31
|
+
}, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
|
|
32
|
+
createdAt: NativeDate;
|
|
33
|
+
updatedAt: NativeDate;
|
|
34
|
+
} & {
|
|
35
|
+
user: {
|
|
36
|
+
prototype?: mongoose.Types.ObjectId;
|
|
37
|
+
cacheHexString?: unknown;
|
|
38
|
+
generate?: {};
|
|
39
|
+
createFromTime?: {};
|
|
40
|
+
createFromHexString?: {};
|
|
41
|
+
createFromBase64?: {};
|
|
42
|
+
isValid?: {};
|
|
43
|
+
};
|
|
44
|
+
monitor: {
|
|
45
|
+
prototype?: mongoose.Types.ObjectId;
|
|
46
|
+
cacheHexString?: unknown;
|
|
47
|
+
generate?: {};
|
|
48
|
+
createFromTime?: {};
|
|
49
|
+
createFromHexString?: {};
|
|
50
|
+
createFromBase64?: {};
|
|
51
|
+
isValid?: {};
|
|
52
|
+
};
|
|
53
|
+
open: boolean;
|
|
54
|
+
startedAt: Date;
|
|
55
|
+
alertCount: number;
|
|
56
|
+
firstNotifiedAt?: Date;
|
|
57
|
+
endedAt?: Date;
|
|
58
|
+
peakStatus?: string;
|
|
59
|
+
}>> & mongoose.FlatRecord<{
|
|
60
|
+
createdAt: NativeDate;
|
|
61
|
+
updatedAt: NativeDate;
|
|
62
|
+
} & {
|
|
63
|
+
user: {
|
|
64
|
+
prototype?: mongoose.Types.ObjectId;
|
|
65
|
+
cacheHexString?: unknown;
|
|
66
|
+
generate?: {};
|
|
67
|
+
createFromTime?: {};
|
|
68
|
+
createFromHexString?: {};
|
|
69
|
+
createFromBase64?: {};
|
|
70
|
+
isValid?: {};
|
|
71
|
+
};
|
|
72
|
+
monitor: {
|
|
73
|
+
prototype?: mongoose.Types.ObjectId;
|
|
74
|
+
cacheHexString?: unknown;
|
|
75
|
+
generate?: {};
|
|
76
|
+
createFromTime?: {};
|
|
77
|
+
createFromHexString?: {};
|
|
78
|
+
createFromBase64?: {};
|
|
79
|
+
isValid?: {};
|
|
80
|
+
};
|
|
81
|
+
open: boolean;
|
|
82
|
+
startedAt: Date;
|
|
83
|
+
alertCount: number;
|
|
84
|
+
firstNotifiedAt?: Date;
|
|
85
|
+
endedAt?: Date;
|
|
86
|
+
peakStatus?: string;
|
|
87
|
+
}> & {
|
|
88
|
+
_id: mongoose.Types.ObjectId;
|
|
89
|
+
}>;
|
|
90
|
+
export const MonitorEpisode: mongoose.Model<{
|
|
91
|
+
createdAt: NativeDate;
|
|
92
|
+
updatedAt: NativeDate;
|
|
93
|
+
} & {
|
|
94
|
+
user: {
|
|
95
|
+
prototype?: mongoose.Types.ObjectId;
|
|
96
|
+
cacheHexString?: unknown;
|
|
97
|
+
generate?: {};
|
|
98
|
+
createFromTime?: {};
|
|
99
|
+
createFromHexString?: {};
|
|
100
|
+
createFromBase64?: {};
|
|
101
|
+
isValid?: {};
|
|
102
|
+
};
|
|
103
|
+
monitor: {
|
|
104
|
+
prototype?: mongoose.Types.ObjectId;
|
|
105
|
+
cacheHexString?: unknown;
|
|
106
|
+
generate?: {};
|
|
107
|
+
createFromTime?: {};
|
|
108
|
+
createFromHexString?: {};
|
|
109
|
+
createFromBase64?: {};
|
|
110
|
+
isValid?: {};
|
|
111
|
+
};
|
|
112
|
+
open: boolean;
|
|
113
|
+
startedAt: Date;
|
|
114
|
+
alertCount: number;
|
|
115
|
+
firstNotifiedAt?: Date;
|
|
116
|
+
endedAt?: Date;
|
|
117
|
+
peakStatus?: string;
|
|
118
|
+
}, {}, {}, {}, mongoose.Document<unknown, {}, {
|
|
119
|
+
createdAt: NativeDate;
|
|
120
|
+
updatedAt: NativeDate;
|
|
121
|
+
} & {
|
|
122
|
+
user: {
|
|
123
|
+
prototype?: mongoose.Types.ObjectId;
|
|
124
|
+
cacheHexString?: unknown;
|
|
125
|
+
generate?: {};
|
|
126
|
+
createFromTime?: {};
|
|
127
|
+
createFromHexString?: {};
|
|
128
|
+
createFromBase64?: {};
|
|
129
|
+
isValid?: {};
|
|
130
|
+
};
|
|
131
|
+
monitor: {
|
|
132
|
+
prototype?: mongoose.Types.ObjectId;
|
|
133
|
+
cacheHexString?: unknown;
|
|
134
|
+
generate?: {};
|
|
135
|
+
createFromTime?: {};
|
|
136
|
+
createFromHexString?: {};
|
|
137
|
+
createFromBase64?: {};
|
|
138
|
+
isValid?: {};
|
|
139
|
+
};
|
|
140
|
+
open: boolean;
|
|
141
|
+
startedAt: Date;
|
|
142
|
+
alertCount: number;
|
|
143
|
+
firstNotifiedAt?: Date;
|
|
144
|
+
endedAt?: Date;
|
|
145
|
+
peakStatus?: string;
|
|
146
|
+
}> & {
|
|
147
|
+
createdAt: NativeDate;
|
|
148
|
+
updatedAt: NativeDate;
|
|
149
|
+
} & {
|
|
150
|
+
user: {
|
|
151
|
+
prototype?: mongoose.Types.ObjectId;
|
|
152
|
+
cacheHexString?: unknown;
|
|
153
|
+
generate?: {};
|
|
154
|
+
createFromTime?: {};
|
|
155
|
+
createFromHexString?: {};
|
|
156
|
+
createFromBase64?: {};
|
|
157
|
+
isValid?: {};
|
|
158
|
+
};
|
|
159
|
+
monitor: {
|
|
160
|
+
prototype?: mongoose.Types.ObjectId;
|
|
161
|
+
cacheHexString?: unknown;
|
|
162
|
+
generate?: {};
|
|
163
|
+
createFromTime?: {};
|
|
164
|
+
createFromHexString?: {};
|
|
165
|
+
createFromBase64?: {};
|
|
166
|
+
isValid?: {};
|
|
167
|
+
};
|
|
168
|
+
open: boolean;
|
|
169
|
+
startedAt: Date;
|
|
170
|
+
alertCount: number;
|
|
171
|
+
firstNotifiedAt?: Date;
|
|
172
|
+
endedAt?: Date;
|
|
173
|
+
peakStatus?: string;
|
|
174
|
+
} & {
|
|
175
|
+
_id: mongoose.Types.ObjectId;
|
|
176
|
+
}, mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
177
|
+
timestamps: true;
|
|
178
|
+
}, {
|
|
179
|
+
createdAt: NativeDate;
|
|
180
|
+
updatedAt: NativeDate;
|
|
181
|
+
} & {
|
|
182
|
+
user: {
|
|
183
|
+
prototype?: mongoose.Types.ObjectId;
|
|
184
|
+
cacheHexString?: unknown;
|
|
185
|
+
generate?: {};
|
|
186
|
+
createFromTime?: {};
|
|
187
|
+
createFromHexString?: {};
|
|
188
|
+
createFromBase64?: {};
|
|
189
|
+
isValid?: {};
|
|
190
|
+
};
|
|
191
|
+
monitor: {
|
|
192
|
+
prototype?: mongoose.Types.ObjectId;
|
|
193
|
+
cacheHexString?: unknown;
|
|
194
|
+
generate?: {};
|
|
195
|
+
createFromTime?: {};
|
|
196
|
+
createFromHexString?: {};
|
|
197
|
+
createFromBase64?: {};
|
|
198
|
+
isValid?: {};
|
|
199
|
+
};
|
|
200
|
+
open: boolean;
|
|
201
|
+
startedAt: Date;
|
|
202
|
+
alertCount: number;
|
|
203
|
+
firstNotifiedAt?: Date;
|
|
204
|
+
endedAt?: Date;
|
|
205
|
+
peakStatus?: string;
|
|
206
|
+
}, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
|
|
207
|
+
createdAt: NativeDate;
|
|
208
|
+
updatedAt: NativeDate;
|
|
209
|
+
} & {
|
|
210
|
+
user: {
|
|
211
|
+
prototype?: mongoose.Types.ObjectId;
|
|
212
|
+
cacheHexString?: unknown;
|
|
213
|
+
generate?: {};
|
|
214
|
+
createFromTime?: {};
|
|
215
|
+
createFromHexString?: {};
|
|
216
|
+
createFromBase64?: {};
|
|
217
|
+
isValid?: {};
|
|
218
|
+
};
|
|
219
|
+
monitor: {
|
|
220
|
+
prototype?: mongoose.Types.ObjectId;
|
|
221
|
+
cacheHexString?: unknown;
|
|
222
|
+
generate?: {};
|
|
223
|
+
createFromTime?: {};
|
|
224
|
+
createFromHexString?: {};
|
|
225
|
+
createFromBase64?: {};
|
|
226
|
+
isValid?: {};
|
|
227
|
+
};
|
|
228
|
+
open: boolean;
|
|
229
|
+
startedAt: Date;
|
|
230
|
+
alertCount: number;
|
|
231
|
+
firstNotifiedAt?: Date;
|
|
232
|
+
endedAt?: Date;
|
|
233
|
+
peakStatus?: string;
|
|
234
|
+
}>> & mongoose.FlatRecord<{
|
|
235
|
+
createdAt: NativeDate;
|
|
236
|
+
updatedAt: NativeDate;
|
|
237
|
+
} & {
|
|
238
|
+
user: {
|
|
239
|
+
prototype?: mongoose.Types.ObjectId;
|
|
240
|
+
cacheHexString?: unknown;
|
|
241
|
+
generate?: {};
|
|
242
|
+
createFromTime?: {};
|
|
243
|
+
createFromHexString?: {};
|
|
244
|
+
createFromBase64?: {};
|
|
245
|
+
isValid?: {};
|
|
246
|
+
};
|
|
247
|
+
monitor: {
|
|
248
|
+
prototype?: mongoose.Types.ObjectId;
|
|
249
|
+
cacheHexString?: unknown;
|
|
250
|
+
generate?: {};
|
|
251
|
+
createFromTime?: {};
|
|
252
|
+
createFromHexString?: {};
|
|
253
|
+
createFromBase64?: {};
|
|
254
|
+
isValid?: {};
|
|
255
|
+
};
|
|
256
|
+
open: boolean;
|
|
257
|
+
startedAt: Date;
|
|
258
|
+
alertCount: number;
|
|
259
|
+
firstNotifiedAt?: Date;
|
|
260
|
+
endedAt?: Date;
|
|
261
|
+
peakStatus?: string;
|
|
262
|
+
}> & {
|
|
263
|
+
_id: mongoose.Types.ObjectId;
|
|
264
|
+
}>>;
|
|
265
|
+
import mongoose from "mongoose";
|
|
266
|
+
//# sourceMappingURL=monitorEpisodes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitorEpisodes.d.ts","sourceRoot":"","sources":["../../src/models/monitorEpisodes.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoBE;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA+E;qBA7E1D,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitors.d.ts","sourceRoot":"","sources":["../../src/models/monitors.js"],"names":[],"mappings":"AA4KA,
|
|
1
|
+
{"version":3,"file":"monitors.d.ts","sourceRoot":"","sources":["../../src/models/monitors.js"],"names":[],"mappings":"AA4KA,iCAkJE;AAuIF,2BAA4D;AAhU5D,qCAiCE;AAGF,+BAAwE;AAtKxE,sCAAoD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitorEpisodes.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/monitorEpisodes.test.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitorsLeftGoodAt.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/monitorsLeftGoodAt.test.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userNotifications.test.d.ts","sourceRoot":"","sources":["../../../src/models/tests/userNotifications.test.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
export const userNotificationsSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
2
|
+
timestamps: true;
|
|
3
|
+
}, {
|
|
4
|
+
createdAt: NativeDate;
|
|
5
|
+
updatedAt: NativeDate;
|
|
6
|
+
} & {
|
|
7
|
+
user: {
|
|
8
|
+
prototype?: mongoose.Types.ObjectId;
|
|
9
|
+
cacheHexString?: unknown;
|
|
10
|
+
generate?: {};
|
|
11
|
+
createFromTime?: {};
|
|
12
|
+
createFromHexString?: {};
|
|
13
|
+
createFromBase64?: {};
|
|
14
|
+
isValid?: {};
|
|
15
|
+
};
|
|
16
|
+
kind: string;
|
|
17
|
+
monitors: {
|
|
18
|
+
prototype?: mongoose.Types.ObjectId;
|
|
19
|
+
cacheHexString?: unknown;
|
|
20
|
+
generate?: {};
|
|
21
|
+
createFromTime?: {};
|
|
22
|
+
createFromHexString?: {};
|
|
23
|
+
createFromBase64?: {};
|
|
24
|
+
isValid?: {};
|
|
25
|
+
}[];
|
|
26
|
+
episodes: {
|
|
27
|
+
prototype?: mongoose.Types.ObjectId;
|
|
28
|
+
cacheHexString?: unknown;
|
|
29
|
+
generate?: {};
|
|
30
|
+
createFromTime?: {};
|
|
31
|
+
createFromHexString?: {};
|
|
32
|
+
createFromBase64?: {};
|
|
33
|
+
isValid?: {};
|
|
34
|
+
}[];
|
|
35
|
+
sentAt: Date;
|
|
36
|
+
message?: string;
|
|
37
|
+
status?: string;
|
|
38
|
+
}, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
|
|
39
|
+
createdAt: NativeDate;
|
|
40
|
+
updatedAt: NativeDate;
|
|
41
|
+
} & {
|
|
42
|
+
user: {
|
|
43
|
+
prototype?: mongoose.Types.ObjectId;
|
|
44
|
+
cacheHexString?: unknown;
|
|
45
|
+
generate?: {};
|
|
46
|
+
createFromTime?: {};
|
|
47
|
+
createFromHexString?: {};
|
|
48
|
+
createFromBase64?: {};
|
|
49
|
+
isValid?: {};
|
|
50
|
+
};
|
|
51
|
+
kind: string;
|
|
52
|
+
monitors: {
|
|
53
|
+
prototype?: mongoose.Types.ObjectId;
|
|
54
|
+
cacheHexString?: unknown;
|
|
55
|
+
generate?: {};
|
|
56
|
+
createFromTime?: {};
|
|
57
|
+
createFromHexString?: {};
|
|
58
|
+
createFromBase64?: {};
|
|
59
|
+
isValid?: {};
|
|
60
|
+
}[];
|
|
61
|
+
episodes: {
|
|
62
|
+
prototype?: mongoose.Types.ObjectId;
|
|
63
|
+
cacheHexString?: unknown;
|
|
64
|
+
generate?: {};
|
|
65
|
+
createFromTime?: {};
|
|
66
|
+
createFromHexString?: {};
|
|
67
|
+
createFromBase64?: {};
|
|
68
|
+
isValid?: {};
|
|
69
|
+
}[];
|
|
70
|
+
sentAt: Date;
|
|
71
|
+
message?: string;
|
|
72
|
+
status?: string;
|
|
73
|
+
}>> & mongoose.FlatRecord<{
|
|
74
|
+
createdAt: NativeDate;
|
|
75
|
+
updatedAt: NativeDate;
|
|
76
|
+
} & {
|
|
77
|
+
user: {
|
|
78
|
+
prototype?: mongoose.Types.ObjectId;
|
|
79
|
+
cacheHexString?: unknown;
|
|
80
|
+
generate?: {};
|
|
81
|
+
createFromTime?: {};
|
|
82
|
+
createFromHexString?: {};
|
|
83
|
+
createFromBase64?: {};
|
|
84
|
+
isValid?: {};
|
|
85
|
+
};
|
|
86
|
+
kind: string;
|
|
87
|
+
monitors: {
|
|
88
|
+
prototype?: mongoose.Types.ObjectId;
|
|
89
|
+
cacheHexString?: unknown;
|
|
90
|
+
generate?: {};
|
|
91
|
+
createFromTime?: {};
|
|
92
|
+
createFromHexString?: {};
|
|
93
|
+
createFromBase64?: {};
|
|
94
|
+
isValid?: {};
|
|
95
|
+
}[];
|
|
96
|
+
episodes: {
|
|
97
|
+
prototype?: mongoose.Types.ObjectId;
|
|
98
|
+
cacheHexString?: unknown;
|
|
99
|
+
generate?: {};
|
|
100
|
+
createFromTime?: {};
|
|
101
|
+
createFromHexString?: {};
|
|
102
|
+
createFromBase64?: {};
|
|
103
|
+
isValid?: {};
|
|
104
|
+
}[];
|
|
105
|
+
sentAt: Date;
|
|
106
|
+
message?: string;
|
|
107
|
+
status?: string;
|
|
108
|
+
}> & {
|
|
109
|
+
_id: mongoose.Types.ObjectId;
|
|
110
|
+
}>;
|
|
111
|
+
export const UserNotification: mongoose.Model<{
|
|
112
|
+
createdAt: NativeDate;
|
|
113
|
+
updatedAt: NativeDate;
|
|
114
|
+
} & {
|
|
115
|
+
user: {
|
|
116
|
+
prototype?: mongoose.Types.ObjectId;
|
|
117
|
+
cacheHexString?: unknown;
|
|
118
|
+
generate?: {};
|
|
119
|
+
createFromTime?: {};
|
|
120
|
+
createFromHexString?: {};
|
|
121
|
+
createFromBase64?: {};
|
|
122
|
+
isValid?: {};
|
|
123
|
+
};
|
|
124
|
+
kind: string;
|
|
125
|
+
monitors: {
|
|
126
|
+
prototype?: mongoose.Types.ObjectId;
|
|
127
|
+
cacheHexString?: unknown;
|
|
128
|
+
generate?: {};
|
|
129
|
+
createFromTime?: {};
|
|
130
|
+
createFromHexString?: {};
|
|
131
|
+
createFromBase64?: {};
|
|
132
|
+
isValid?: {};
|
|
133
|
+
}[];
|
|
134
|
+
episodes: {
|
|
135
|
+
prototype?: mongoose.Types.ObjectId;
|
|
136
|
+
cacheHexString?: unknown;
|
|
137
|
+
generate?: {};
|
|
138
|
+
createFromTime?: {};
|
|
139
|
+
createFromHexString?: {};
|
|
140
|
+
createFromBase64?: {};
|
|
141
|
+
isValid?: {};
|
|
142
|
+
}[];
|
|
143
|
+
sentAt: Date;
|
|
144
|
+
message?: string;
|
|
145
|
+
status?: string;
|
|
146
|
+
}, {}, {}, {}, mongoose.Document<unknown, {}, {
|
|
147
|
+
createdAt: NativeDate;
|
|
148
|
+
updatedAt: NativeDate;
|
|
149
|
+
} & {
|
|
150
|
+
user: {
|
|
151
|
+
prototype?: mongoose.Types.ObjectId;
|
|
152
|
+
cacheHexString?: unknown;
|
|
153
|
+
generate?: {};
|
|
154
|
+
createFromTime?: {};
|
|
155
|
+
createFromHexString?: {};
|
|
156
|
+
createFromBase64?: {};
|
|
157
|
+
isValid?: {};
|
|
158
|
+
};
|
|
159
|
+
kind: string;
|
|
160
|
+
monitors: {
|
|
161
|
+
prototype?: mongoose.Types.ObjectId;
|
|
162
|
+
cacheHexString?: unknown;
|
|
163
|
+
generate?: {};
|
|
164
|
+
createFromTime?: {};
|
|
165
|
+
createFromHexString?: {};
|
|
166
|
+
createFromBase64?: {};
|
|
167
|
+
isValid?: {};
|
|
168
|
+
}[];
|
|
169
|
+
episodes: {
|
|
170
|
+
prototype?: mongoose.Types.ObjectId;
|
|
171
|
+
cacheHexString?: unknown;
|
|
172
|
+
generate?: {};
|
|
173
|
+
createFromTime?: {};
|
|
174
|
+
createFromHexString?: {};
|
|
175
|
+
createFromBase64?: {};
|
|
176
|
+
isValid?: {};
|
|
177
|
+
}[];
|
|
178
|
+
sentAt: Date;
|
|
179
|
+
message?: string;
|
|
180
|
+
status?: string;
|
|
181
|
+
}> & {
|
|
182
|
+
createdAt: NativeDate;
|
|
183
|
+
updatedAt: NativeDate;
|
|
184
|
+
} & {
|
|
185
|
+
user: {
|
|
186
|
+
prototype?: mongoose.Types.ObjectId;
|
|
187
|
+
cacheHexString?: unknown;
|
|
188
|
+
generate?: {};
|
|
189
|
+
createFromTime?: {};
|
|
190
|
+
createFromHexString?: {};
|
|
191
|
+
createFromBase64?: {};
|
|
192
|
+
isValid?: {};
|
|
193
|
+
};
|
|
194
|
+
kind: string;
|
|
195
|
+
monitors: {
|
|
196
|
+
prototype?: mongoose.Types.ObjectId;
|
|
197
|
+
cacheHexString?: unknown;
|
|
198
|
+
generate?: {};
|
|
199
|
+
createFromTime?: {};
|
|
200
|
+
createFromHexString?: {};
|
|
201
|
+
createFromBase64?: {};
|
|
202
|
+
isValid?: {};
|
|
203
|
+
}[];
|
|
204
|
+
episodes: {
|
|
205
|
+
prototype?: mongoose.Types.ObjectId;
|
|
206
|
+
cacheHexString?: unknown;
|
|
207
|
+
generate?: {};
|
|
208
|
+
createFromTime?: {};
|
|
209
|
+
createFromHexString?: {};
|
|
210
|
+
createFromBase64?: {};
|
|
211
|
+
isValid?: {};
|
|
212
|
+
}[];
|
|
213
|
+
sentAt: Date;
|
|
214
|
+
message?: string;
|
|
215
|
+
status?: string;
|
|
216
|
+
} & {
|
|
217
|
+
_id: mongoose.Types.ObjectId;
|
|
218
|
+
}, mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, {}, {}, {}, {}, {
|
|
219
|
+
timestamps: true;
|
|
220
|
+
}, {
|
|
221
|
+
createdAt: NativeDate;
|
|
222
|
+
updatedAt: NativeDate;
|
|
223
|
+
} & {
|
|
224
|
+
user: {
|
|
225
|
+
prototype?: mongoose.Types.ObjectId;
|
|
226
|
+
cacheHexString?: unknown;
|
|
227
|
+
generate?: {};
|
|
228
|
+
createFromTime?: {};
|
|
229
|
+
createFromHexString?: {};
|
|
230
|
+
createFromBase64?: {};
|
|
231
|
+
isValid?: {};
|
|
232
|
+
};
|
|
233
|
+
kind: string;
|
|
234
|
+
monitors: {
|
|
235
|
+
prototype?: mongoose.Types.ObjectId;
|
|
236
|
+
cacheHexString?: unknown;
|
|
237
|
+
generate?: {};
|
|
238
|
+
createFromTime?: {};
|
|
239
|
+
createFromHexString?: {};
|
|
240
|
+
createFromBase64?: {};
|
|
241
|
+
isValid?: {};
|
|
242
|
+
}[];
|
|
243
|
+
episodes: {
|
|
244
|
+
prototype?: mongoose.Types.ObjectId;
|
|
245
|
+
cacheHexString?: unknown;
|
|
246
|
+
generate?: {};
|
|
247
|
+
createFromTime?: {};
|
|
248
|
+
createFromHexString?: {};
|
|
249
|
+
createFromBase64?: {};
|
|
250
|
+
isValid?: {};
|
|
251
|
+
}[];
|
|
252
|
+
sentAt: Date;
|
|
253
|
+
message?: string;
|
|
254
|
+
status?: string;
|
|
255
|
+
}, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
|
|
256
|
+
createdAt: NativeDate;
|
|
257
|
+
updatedAt: NativeDate;
|
|
258
|
+
} & {
|
|
259
|
+
user: {
|
|
260
|
+
prototype?: mongoose.Types.ObjectId;
|
|
261
|
+
cacheHexString?: unknown;
|
|
262
|
+
generate?: {};
|
|
263
|
+
createFromTime?: {};
|
|
264
|
+
createFromHexString?: {};
|
|
265
|
+
createFromBase64?: {};
|
|
266
|
+
isValid?: {};
|
|
267
|
+
};
|
|
268
|
+
kind: string;
|
|
269
|
+
monitors: {
|
|
270
|
+
prototype?: mongoose.Types.ObjectId;
|
|
271
|
+
cacheHexString?: unknown;
|
|
272
|
+
generate?: {};
|
|
273
|
+
createFromTime?: {};
|
|
274
|
+
createFromHexString?: {};
|
|
275
|
+
createFromBase64?: {};
|
|
276
|
+
isValid?: {};
|
|
277
|
+
}[];
|
|
278
|
+
episodes: {
|
|
279
|
+
prototype?: mongoose.Types.ObjectId;
|
|
280
|
+
cacheHexString?: unknown;
|
|
281
|
+
generate?: {};
|
|
282
|
+
createFromTime?: {};
|
|
283
|
+
createFromHexString?: {};
|
|
284
|
+
createFromBase64?: {};
|
|
285
|
+
isValid?: {};
|
|
286
|
+
}[];
|
|
287
|
+
sentAt: Date;
|
|
288
|
+
message?: string;
|
|
289
|
+
status?: string;
|
|
290
|
+
}>> & mongoose.FlatRecord<{
|
|
291
|
+
createdAt: NativeDate;
|
|
292
|
+
updatedAt: NativeDate;
|
|
293
|
+
} & {
|
|
294
|
+
user: {
|
|
295
|
+
prototype?: mongoose.Types.ObjectId;
|
|
296
|
+
cacheHexString?: unknown;
|
|
297
|
+
generate?: {};
|
|
298
|
+
createFromTime?: {};
|
|
299
|
+
createFromHexString?: {};
|
|
300
|
+
createFromBase64?: {};
|
|
301
|
+
isValid?: {};
|
|
302
|
+
};
|
|
303
|
+
kind: string;
|
|
304
|
+
monitors: {
|
|
305
|
+
prototype?: mongoose.Types.ObjectId;
|
|
306
|
+
cacheHexString?: unknown;
|
|
307
|
+
generate?: {};
|
|
308
|
+
createFromTime?: {};
|
|
309
|
+
createFromHexString?: {};
|
|
310
|
+
createFromBase64?: {};
|
|
311
|
+
isValid?: {};
|
|
312
|
+
}[];
|
|
313
|
+
episodes: {
|
|
314
|
+
prototype?: mongoose.Types.ObjectId;
|
|
315
|
+
cacheHexString?: unknown;
|
|
316
|
+
generate?: {};
|
|
317
|
+
createFromTime?: {};
|
|
318
|
+
createFromHexString?: {};
|
|
319
|
+
createFromBase64?: {};
|
|
320
|
+
isValid?: {};
|
|
321
|
+
}[];
|
|
322
|
+
sentAt: Date;
|
|
323
|
+
message?: string;
|
|
324
|
+
status?: string;
|
|
325
|
+
}> & {
|
|
326
|
+
_id: mongoose.Types.ObjectId;
|
|
327
|
+
}>>;
|
|
328
|
+
export const NOTIFICATION_KINDS: string[];
|
|
329
|
+
import mongoose from "mongoose";
|
|
330
|
+
//# sourceMappingURL=userNotifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userNotifications.d.ts","sourceRoot":"","sources":["../../src/models/userNotifications.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAeE;AAWF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGE;AAjCF,0CAAoD;qBAf/B,UAAU"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justair/justair-library",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.12.0-alpha.25e595a",
|
|
4
4
|
"description": "JustAir Internal Library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"default": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"./constants": {
|
|
14
|
-
"types": "./dist/constants/
|
|
15
|
-
"default": "./src/constants/
|
|
14
|
+
"types": "./dist/constants/index.d.ts",
|
|
15
|
+
"default": "./src/constants/index.js"
|
|
16
16
|
},
|
|
17
17
|
"./auth": {
|
|
18
18
|
"types": "./dist/modules/auth/index.d.ts",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// AQI alert-level hierarchy — single source of truth (JA-2658, eng review
|
|
2
|
+
// issue 7). Consumers: JustAirNotificationService statusAlerts threshold
|
|
3
|
+
// logic, CalculateAQIService determineHighestAlertLevel, and the
|
|
4
|
+
// MonitorEpisode/UserNotification schema enums.
|
|
5
|
+
//
|
|
6
|
+
// DO NOT change these values — the integers map directly to user
|
|
7
|
+
// alertPreferenceLevel thresholds and Courier email template selection.
|
|
8
|
+
|
|
9
|
+
export const ALERT_LEVEL_WEIGHTS = {
|
|
10
|
+
Good: 1,
|
|
11
|
+
Moderate: 2,
|
|
12
|
+
"Unhealthy for SG": 3,
|
|
13
|
+
Unhealthy: 4,
|
|
14
|
+
"Very Unhealthy": 5,
|
|
15
|
+
Hazardous: 6,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Weight lookup with the platform's long-standing fallback: unknown statuses
|
|
19
|
+
// weigh the same as Good, i.e. they never alert and never open episodes.
|
|
20
|
+
// This deliberately covers the legacy "Bad" value still present in the
|
|
21
|
+
// monitorAlertStatus enum (monitors.js) — candidate for a data migration,
|
|
22
|
+
// tracked in JustAirNotificationService/TODOS.md context.
|
|
23
|
+
export const getAlertWeight = (alertLevel) =>
|
|
24
|
+
ALERT_LEVEL_WEIGHTS[alertLevel] || ALERT_LEVEL_WEIGHTS.Good;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Barrel for the `@justair/justair-library/constants` entry point.
|
|
2
|
+
//
|
|
3
|
+
// Backward compatibility contract: this file MUST re-export everything
|
|
4
|
+
// pollutants.js exports, because package.json's `./constants` subpath pointed
|
|
5
|
+
// straight at pollutants.js before alertLevels.js existed — existing
|
|
6
|
+
// `import { PARAMETERS, HEAVY_METALS } from "@justair/justair-library/constants"`
|
|
7
|
+
// call sites across the platform must keep working unchanged.
|
|
8
|
+
// (Verified by src/constants/tests/constantsBarrel.test.js.)
|
|
9
|
+
|
|
10
|
+
export * from "./pollutants.js";
|
|
11
|
+
export * from "./alertLevels.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The `./constants` package subpath used to point directly at pollutants.js.
|
|
2
|
+
// It now points at the index.js barrel so alertLevels.js can share the entry
|
|
3
|
+
// point. These tests pin the backward-compatibility contract: every consumer
|
|
4
|
+
// importing PARAMETERS / HEAVY_METALS through the barrel must see the exact
|
|
5
|
+
// same objects as before.
|
|
6
|
+
import * as barrel from '../index.js';
|
|
7
|
+
import { PARAMETERS, HEAVY_METALS } from '../pollutants.js';
|
|
8
|
+
import { ALERT_LEVEL_WEIGHTS, getAlertWeight } from '../alertLevels.js';
|
|
9
|
+
|
|
10
|
+
test('barrel re-exports PARAMETERS and HEAVY_METALS unchanged (identity, not copies)', () => {
|
|
11
|
+
expect(barrel.PARAMETERS).toBe(PARAMETERS);
|
|
12
|
+
expect(barrel.HEAVY_METALS).toBe(HEAVY_METALS);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('barrel exports the alert-level constants', () => {
|
|
16
|
+
expect(barrel.ALERT_LEVEL_WEIGHTS).toBe(ALERT_LEVEL_WEIGHTS);
|
|
17
|
+
expect(barrel.getAlertWeight).toBe(getAlertWeight);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('ALERT_LEVEL_WEIGHTS carries the exact platform hierarchy (values map to thresholds + Courier templates)', () => {
|
|
21
|
+
expect(ALERT_LEVEL_WEIGHTS).toEqual({
|
|
22
|
+
Good: 1,
|
|
23
|
+
Moderate: 2,
|
|
24
|
+
'Unhealthy for SG': 3,
|
|
25
|
+
Unhealthy: 4,
|
|
26
|
+
'Very Unhealthy': 5,
|
|
27
|
+
Hazardous: 6,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('getAlertWeight resolves every known level', () => {
|
|
32
|
+
expect(getAlertWeight('Good')).toBe(1);
|
|
33
|
+
expect(getAlertWeight('Moderate')).toBe(2);
|
|
34
|
+
expect(getAlertWeight('Unhealthy for SG')).toBe(3);
|
|
35
|
+
expect(getAlertWeight('Unhealthy')).toBe(4);
|
|
36
|
+
expect(getAlertWeight('Very Unhealthy')).toBe(5);
|
|
37
|
+
expect(getAlertWeight('Hazardous')).toBe(6);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('unknown and legacy statuses fall back to the Good weight (never alert, never open episodes)', () => {
|
|
41
|
+
// "Bad" still exists in the monitorAlertStatus enum — deliberately non-alerting.
|
|
42
|
+
expect(getAlertWeight('Bad')).toBe(1);
|
|
43
|
+
expect(getAlertWeight(undefined)).toBe(1);
|
|
44
|
+
expect(getAlertWeight(null)).toBe(1);
|
|
45
|
+
expect(getAlertWeight('')).toBe(1);
|
|
46
|
+
});
|
package/src/index.js
CHANGED
|
@@ -66,6 +66,16 @@ import {
|
|
|
66
66
|
import Database from "./config/db.js"; // Import the new Database class
|
|
67
67
|
import CustomLogger from "./config/logger.js";
|
|
68
68
|
import { PARAMETERS, HEAVY_METALS } from "./constants/pollutants.js";
|
|
69
|
+
import { ALERT_LEVEL_WEIGHTS, getAlertWeight } from "./constants/alertLevels.js";
|
|
70
|
+
import {
|
|
71
|
+
monitorEpisodesSchema,
|
|
72
|
+
MonitorEpisode,
|
|
73
|
+
} from "./models/monitorEpisodes.js";
|
|
74
|
+
import {
|
|
75
|
+
userNotificationsSchema,
|
|
76
|
+
UserNotification,
|
|
77
|
+
NOTIFICATION_KINDS,
|
|
78
|
+
} from "./models/userNotifications.js";
|
|
69
79
|
import { splitName } from "./utils/splitName.js";
|
|
70
80
|
|
|
71
81
|
// Sites related imports
|
|
@@ -171,9 +181,18 @@ export {
|
|
|
171
181
|
// Password reset tokens (JA-2946)
|
|
172
182
|
passwordResetTokenSchema,
|
|
173
183
|
PasswordResetToken,
|
|
184
|
+
// Notification episodes + send log (JA-2658 / JA-2691 stack)
|
|
185
|
+
monitorEpisodesSchema,
|
|
186
|
+
MonitorEpisode,
|
|
187
|
+
userNotificationsSchema,
|
|
188
|
+
UserNotification,
|
|
189
|
+
NOTIFICATION_KINDS,
|
|
174
190
|
// Pollutant constants
|
|
175
191
|
PARAMETERS,
|
|
176
192
|
HEAVY_METALS,
|
|
193
|
+
// AQI alert-level hierarchy (single source of truth)
|
|
194
|
+
ALERT_LEVEL_WEIGHTS,
|
|
195
|
+
getAlertWeight,
|
|
177
196
|
// Utilities
|
|
178
197
|
splitName,
|
|
179
198
|
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
import { ALERT_LEVEL_WEIGHTS } from "../constants/alertLevels.js";
|
|
3
|
+
|
|
4
|
+
// MonitorEpisode — one mutable doc per OPEN episode per user+device (JA-2658).
|
|
5
|
+
// Written by JustAirNotificationService's alert cron (JA-2958); read by
|
|
6
|
+
// JustAirUserService GET /users/my-monitors (JA-2691).
|
|
7
|
+
//
|
|
8
|
+
// Good→nonGood (cron observes; startedAt = Monitors.leftGoodAt ?? now)
|
|
9
|
+
// (no doc) ───────────────────────────────► OPEN {open:true, startedAt, peakStatus}
|
|
10
|
+
// │ alert event names this monitor
|
|
11
|
+
// ▼
|
|
12
|
+
// OPEN {firstNotifiedAt set once, alertCount++}
|
|
13
|
+
// nonGood→Good / sweep │
|
|
14
|
+
// TTL 24mo ◄── CLOSED {open:false, endedAt} ◄┘
|
|
15
|
+
//
|
|
16
|
+
// Conventions the indexes DEPEND on — do not relax:
|
|
17
|
+
// - `open` is the lifecycle state field and the partial-index key. MongoDB
|
|
18
|
+
// partialFilterExpression does NOT support `$exists: false`, so open-ness
|
|
19
|
+
// must be an explicit boolean, not the absence of endedAt. Close = one
|
|
20
|
+
// atomic update: { $set: { open: false, endedAt: <now> } }.
|
|
21
|
+
// - endedAt is OMITTED while open (never null) and set exactly once on close;
|
|
22
|
+
// the TTL index only ever sees closed docs because open docs carry no date.
|
|
23
|
+
// - firstNotifiedAt is OMITTED until the first alert event that names this
|
|
24
|
+
// monitor; stamps must be only-if-unset so retries can't move the time.
|
|
25
|
+
// - startedAt is the best-known "left Good" time: precise when CalcAQI has
|
|
26
|
+
// stamped Monitors.leftGoodAt, first-observation time otherwise (day-one
|
|
27
|
+
// opens and stale monitors carry the cron-observed fallback).
|
|
28
|
+
|
|
29
|
+
const alertLevelsEnum = [...Object.keys(ALERT_LEVEL_WEIGHTS)];
|
|
30
|
+
|
|
31
|
+
const monitorEpisodesSchema = new mongoose.Schema(
|
|
32
|
+
{
|
|
33
|
+
user: {
|
|
34
|
+
type: mongoose.Types.ObjectId,
|
|
35
|
+
ref: "Users",
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
monitor: {
|
|
39
|
+
type: mongoose.Types.ObjectId,
|
|
40
|
+
ref: "Monitors",
|
|
41
|
+
required: true,
|
|
42
|
+
},
|
|
43
|
+
open: { type: Boolean, default: true, required: true },
|
|
44
|
+
startedAt: { type: Date, required: true },
|
|
45
|
+
firstNotifiedAt: { type: Date },
|
|
46
|
+
endedAt: { type: Date },
|
|
47
|
+
alertCount: { type: Number, default: 0 },
|
|
48
|
+
peakStatus: { type: String, enum: alertLevelsEnum },
|
|
49
|
+
},
|
|
50
|
+
{ timestamps: true },
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Hot path + correctness guarantee: at most ONE open episode per user+device.
|
|
54
|
+
// Duplicate opens from overlapping cron runs surface as E11000 — writers use
|
|
55
|
+
// ordered:false bulkWrite and tolerate the duplicate as a lost race.
|
|
56
|
+
monitorEpisodesSchema.index(
|
|
57
|
+
{ user: 1, monitor: 1 },
|
|
58
|
+
{ unique: true, partialFilterExpression: { open: true } },
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// JA-2691 read: all open episodes for one user — query { user, open: true }.
|
|
62
|
+
monitorEpisodesSchema.index(
|
|
63
|
+
{ user: 1 },
|
|
64
|
+
{ partialFilterExpression: { open: true } },
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// JA-2958 lifecycle sweep: fetch/inspect all open episodes without scanning
|
|
68
|
+
// 24 months of closed docs — query includes { open: true }.
|
|
69
|
+
monitorEpisodesSchema.index(
|
|
70
|
+
{ monitor: 1 },
|
|
71
|
+
{ partialFilterExpression: { open: true } },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Retention: closed episodes expire ~24 months after endedAt. Open episodes
|
|
75
|
+
// carry no endedAt date, so the TTL monitor never touches them.
|
|
76
|
+
monitorEpisodesSchema.index({ endedAt: 1 }, { expireAfterSeconds: 63113904 });
|
|
77
|
+
|
|
78
|
+
const MonitorEpisode = mongoose.model("MonitorEpisode", monitorEpisodesSchema);
|
|
79
|
+
|
|
80
|
+
export { monitorEpisodesSchema, MonitorEpisode };
|
package/src/models/monitors.js
CHANGED
|
@@ -198,6 +198,12 @@ const monitorsSchema = mongoose.Schema(
|
|
|
198
198
|
monitorType: String,
|
|
199
199
|
monitorIdFromSupplier: String,
|
|
200
200
|
measurementUpdate: Date,
|
|
201
|
+
// When the device last crossed Good→nonGood, stamped by CalculateAQIService
|
|
202
|
+
// updateMonitorStatus and $unset on nonGood→Good recovery — so the field
|
|
203
|
+
// being PRESENT means "currently nonGood since this time". Absent = never
|
|
204
|
+
// stamped (or currently Good). Read by JustAirNotificationService as the
|
|
205
|
+
// MonitorEpisode startedAt source (JA-2691 stack). No default on purpose.
|
|
206
|
+
leftGoodAt: Date,
|
|
201
207
|
monitorProperties: Object,
|
|
202
208
|
isPrivate: { type: Boolean, default: false, required: true },
|
|
203
209
|
monitorState: {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
import { ALERT_LEVEL_WEIGHTS } from "../constants/alertLevels.js";
|
|
3
|
+
|
|
4
|
+
// UserNotification — append-only send log, one doc per outbound notification
|
|
5
|
+
// per channel (JA-2658). Written best-effort by JustAirNotificationService
|
|
6
|
+
// after a send is queued (JA-2958); never blocks or gates the send itself.
|
|
7
|
+
//
|
|
8
|
+
// Semantics (locked in the 2026-07-07 eng review):
|
|
9
|
+
// - One alert EVENT (the per-user threshold gate firing) produces one row PER
|
|
10
|
+
// channel actually sent (kind aqi-sms / aqi-email). Episode alertCount is
|
|
11
|
+
// incremented once per EVENT — reconcile counts against events, not rows.
|
|
12
|
+
// - `monitors` lists exactly the monitors named in the message
|
|
13
|
+
// (monitorsAtHighestStatus), and `episodes` the matching MonitorEpisode
|
|
14
|
+
// _ids — a row is 1:1 reconcilable with the SMS/email content.
|
|
15
|
+
|
|
16
|
+
const NOTIFICATION_KINDS = ["aqi-sms", "aqi-email"];
|
|
17
|
+
|
|
18
|
+
const alertLevelsEnum = [...Object.keys(ALERT_LEVEL_WEIGHTS)];
|
|
19
|
+
|
|
20
|
+
const userNotificationsSchema = new mongoose.Schema(
|
|
21
|
+
{
|
|
22
|
+
user: {
|
|
23
|
+
type: mongoose.Types.ObjectId,
|
|
24
|
+
ref: "Users",
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
kind: { type: String, enum: NOTIFICATION_KINDS, required: true },
|
|
28
|
+
monitors: [{ type: mongoose.Types.ObjectId, ref: "Monitors" }],
|
|
29
|
+
episodes: [{ type: mongoose.Types.ObjectId, ref: "MonitorEpisode" }],
|
|
30
|
+
status: { type: String, enum: alertLevelsEnum },
|
|
31
|
+
message: String,
|
|
32
|
+
sentAt: { type: Date, default: Date.now, required: true },
|
|
33
|
+
},
|
|
34
|
+
{ timestamps: true },
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Per-user history, newest first.
|
|
38
|
+
userNotificationsSchema.index({ user: 1, sentAt: -1 });
|
|
39
|
+
|
|
40
|
+
// Device-scoped history for one user (multikey on monitors).
|
|
41
|
+
userNotificationsSchema.index({ user: 1, monitors: 1, sentAt: -1 });
|
|
42
|
+
|
|
43
|
+
// Retention: rows expire ~24 months after send.
|
|
44
|
+
userNotificationsSchema.index({ sentAt: 1 }, { expireAfterSeconds: 63113904 });
|
|
45
|
+
|
|
46
|
+
const UserNotification = mongoose.model(
|
|
47
|
+
"UserNotification",
|
|
48
|
+
userNotificationsSchema,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export { userNotificationsSchema, UserNotification, NOTIFICATION_KINDS };
|