@internetderdinge/api 1.229.37 → 1.229.40
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/src/admin/adminSearch.route.js +4 -0
- package/dist/src/devices/devices.controller.js +3 -0
- package/dist/src/devices/devices.route.js +1 -1
- package/dist/src/devices/devices.validation.js +15 -7
- package/dist/src/iotdevice/iotdevice.service.js +5 -5
- package/dist/src/iotdevice/iotdevice.validation.js +12 -4
- package/dist/src/utils/zValidations.js +7 -0
- package/package.json +1 -1
- package/src/admin/adminSearch.route.ts +4 -0
- package/src/devices/devices.controller.ts +4 -0
- package/src/devices/devices.route.ts +1 -1
- package/src/devices/devices.validation.ts +20 -7
- package/src/iotdevice/iotdevice.service.ts +8 -7
- package/src/iotdevice/iotdevice.validation.ts +12 -4
- package/src/utils/zValidations.ts +8 -0
|
@@ -13,6 +13,7 @@ export const adminSearchRouteSpecs = [
|
|
|
13
13
|
requestSchema: adminStatsSchema,
|
|
14
14
|
responseSchema: adminStatsResponseSchema,
|
|
15
15
|
handler: getStats,
|
|
16
|
+
privateDocs: true,
|
|
16
17
|
summary: "Get admin stats",
|
|
17
18
|
description: "Returns total counts for organizations, users, and devices.",
|
|
18
19
|
},
|
|
@@ -24,6 +25,7 @@ export const adminSearchRouteSpecs = [
|
|
|
24
25
|
responseSchema: adminSearchResponseSchema,
|
|
25
26
|
handler: searchAdmin,
|
|
26
27
|
summary: "Search organizations, users, and devices",
|
|
28
|
+
privateDocs: true,
|
|
27
29
|
description: "Performs an admin-only global search over organizations, users, and devices.",
|
|
28
30
|
},
|
|
29
31
|
{
|
|
@@ -33,6 +35,7 @@ export const adminSearchRouteSpecs = [
|
|
|
33
35
|
requestSchema: adminIotDevicesSchema,
|
|
34
36
|
responseSchema: adminIotDevicesResponseSchema,
|
|
35
37
|
handler: getIotDevices,
|
|
38
|
+
privateDocs: true,
|
|
36
39
|
summary: "List IoT devices",
|
|
37
40
|
description: "Returns the IoT device status list used for admin device/order sync workflows.",
|
|
38
41
|
},
|
|
@@ -43,6 +46,7 @@ export const adminSearchRouteSpecs = [
|
|
|
43
46
|
requestSchema: adminDevicesSchema,
|
|
44
47
|
responseSchema: adminDevicesResponseSchema,
|
|
45
48
|
handler: getDevices,
|
|
49
|
+
privateDocs: true,
|
|
46
50
|
summary: "List MongoDB devices",
|
|
47
51
|
description: "Returns all device documents from MongoDB for admin sync workflows.",
|
|
48
52
|
},
|
|
@@ -107,6 +107,9 @@ const updateEntry = catchAsync(async (req, res) => {
|
|
|
107
107
|
});
|
|
108
108
|
const getEvents = catchAsync(async (req, res) => {
|
|
109
109
|
const device = await devicesService.getById(req.params.deviceId);
|
|
110
|
+
if (!device) {
|
|
111
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Device not found");
|
|
112
|
+
}
|
|
110
113
|
const events = await iotDevicesService.getEvents({
|
|
111
114
|
...req.query,
|
|
112
115
|
createdAt: device.createdAt,
|
|
@@ -32,7 +32,7 @@ export const devicesRouteSpecs = [
|
|
|
32
32
|
responseSchema: devicesResponseSchema,
|
|
33
33
|
handler: devicesController.queryDevicesByUser,
|
|
34
34
|
summary: "Query devices by user",
|
|
35
|
-
description: "Retrieve a paginated list of devices visible to the authenticated user.",
|
|
35
|
+
description: "Retrieve a paginated list of devices visible to the authenticated user. Either patient or organization is required.",
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
method: "get",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { zGet, zObjectId, zObjectIdFor, zPatchBody, zUpdate, zDelete, zPagination, } from "../utils/zValidations.js";
|
|
3
|
+
import { zGet, zObjectId, zObjectIdFor, zPatchBody, zUpdate, zDelete, zPagination, zTypeFilter, } from "../utils/zValidations.js";
|
|
4
4
|
extendZodWithOpenApi(z);
|
|
5
5
|
export const createEntrySchema = {
|
|
6
6
|
body: z
|
|
@@ -58,9 +58,9 @@ export const getEventsSchema = {
|
|
|
58
58
|
}),
|
|
59
59
|
query: z
|
|
60
60
|
.object({
|
|
61
|
-
DateStart: z.string(),
|
|
62
|
-
DateEnd: z.string(),
|
|
63
|
-
TypeFilter:
|
|
61
|
+
DateStart: z.string().datetime().optional(),
|
|
62
|
+
DateEnd: z.string().datetime().optional(),
|
|
63
|
+
TypeFilter: zTypeFilter,
|
|
64
64
|
})
|
|
65
65
|
.openapi({ description: "Fetch device events in a time range" }),
|
|
66
66
|
};
|
|
@@ -144,9 +144,17 @@ export const registerDeviceSchema = {
|
|
|
144
144
|
};
|
|
145
145
|
export const queryDevicesSchema = {
|
|
146
146
|
...zPagination,
|
|
147
|
-
query: zPagination.query
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
query: zPagination.query
|
|
148
|
+
.extend({
|
|
149
|
+
patient: zObjectIdFor("patient").optional().openapi({
|
|
150
|
+
description: "Patient ObjectId. Required if organization is not provided.",
|
|
151
|
+
}),
|
|
152
|
+
organization: zObjectIdFor("organization").optional().openapi({
|
|
153
|
+
description: "Organization ObjectId. Required if patient is not provided.",
|
|
154
|
+
}),
|
|
155
|
+
})
|
|
156
|
+
.refine((data) => data.patient || data.organization, {
|
|
157
|
+
message: "Either patient or organization is required",
|
|
150
158
|
}),
|
|
151
159
|
};
|
|
152
160
|
export const subscriptionSchema = {
|
|
@@ -22,16 +22,16 @@ const iotDataClient = new IoTDataPlaneClient({
|
|
|
22
22
|
*/
|
|
23
23
|
export const getEvents = async (params) => {
|
|
24
24
|
const accessToken = await getAuth0Token();
|
|
25
|
+
// Keep the requested start date, but never query events before the device existed.
|
|
26
|
+
const dateStart = params.createdAt && new Date(params.DateStart) < new Date(params.createdAt)
|
|
27
|
+
? params.createdAt
|
|
28
|
+
: params.DateStart;
|
|
25
29
|
try {
|
|
26
30
|
const response = await axios.get(`${process.env.IOT_API_URL}getevent`, {
|
|
27
31
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
28
32
|
params: {
|
|
29
33
|
DeviceId: params.DeviceId,
|
|
30
|
-
DateStart:
|
|
31
|
-
? params.DateStart
|
|
32
|
-
: params.DateStart < params.createdAt
|
|
33
|
-
? params.DateStart
|
|
34
|
-
: params.createdAt,
|
|
34
|
+
DateStart: dateStart,
|
|
35
35
|
DateEnd: params.DateEnd,
|
|
36
36
|
TypeFilter: params.TypeFilter,
|
|
37
37
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { zObjectId, zPagination } from '../utils/zValidations';
|
|
3
|
+
import { zObjectId, zPagination, zTypeFilter } from '../utils/zValidations';
|
|
4
4
|
extendZodWithOpenApi(z);
|
|
5
5
|
export const getDevice = {
|
|
6
6
|
params: z.object({
|
|
@@ -27,9 +27,17 @@ export const getEventsSchema = {
|
|
|
27
27
|
deviceId: zObjectId.openapi({ description: 'Device ObjectId' }),
|
|
28
28
|
}),
|
|
29
29
|
query: z.object({
|
|
30
|
-
DateStart: z
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
DateStart: z
|
|
31
|
+
.string()
|
|
32
|
+
.datetime()
|
|
33
|
+
.openapi({ description: 'Start date (ISO‐string)', example: '2025-05-01T00:00:00Z' })
|
|
34
|
+
.optional(),
|
|
35
|
+
DateEnd: z
|
|
36
|
+
.string()
|
|
37
|
+
.datetime()
|
|
38
|
+
.openapi({ description: 'End date (ISO‐string)', example: '2025-05-31T23:59:59Z' })
|
|
39
|
+
.optional(),
|
|
40
|
+
TypeFilter: zTypeFilter.default(''),
|
|
33
41
|
}),
|
|
34
42
|
};
|
|
35
43
|
export const updateEntrySchema = {};
|
|
@@ -141,3 +141,10 @@ export const zDelete = (id) => ({
|
|
|
141
141
|
});
|
|
142
142
|
export const zObjectId = zObjectIdFor();
|
|
143
143
|
export const zDate = () => z.string().pipe(z.coerce.date());
|
|
144
|
+
export const zTypeFilter = z
|
|
145
|
+
.string()
|
|
146
|
+
.openapi({
|
|
147
|
+
description: "Event type filter. Common values include activate and state; any other string is accepted.",
|
|
148
|
+
example: "activate",
|
|
149
|
+
})
|
|
150
|
+
.optional();
|
package/package.json
CHANGED
|
@@ -31,6 +31,7 @@ export const adminSearchRouteSpecs: RouteSpec[] = [
|
|
|
31
31
|
requestSchema: adminStatsSchema,
|
|
32
32
|
responseSchema: adminStatsResponseSchema,
|
|
33
33
|
handler: getStats,
|
|
34
|
+
privateDocs: true,
|
|
34
35
|
summary: "Get admin stats",
|
|
35
36
|
description: "Returns total counts for organizations, users, and devices.",
|
|
36
37
|
},
|
|
@@ -42,6 +43,7 @@ export const adminSearchRouteSpecs: RouteSpec[] = [
|
|
|
42
43
|
responseSchema: adminSearchResponseSchema,
|
|
43
44
|
handler: searchAdmin,
|
|
44
45
|
summary: "Search organizations, users, and devices",
|
|
46
|
+
privateDocs: true,
|
|
45
47
|
description:
|
|
46
48
|
"Performs an admin-only global search over organizations, users, and devices.",
|
|
47
49
|
},
|
|
@@ -52,6 +54,7 @@ export const adminSearchRouteSpecs: RouteSpec[] = [
|
|
|
52
54
|
requestSchema: adminIotDevicesSchema,
|
|
53
55
|
responseSchema: adminIotDevicesResponseSchema,
|
|
54
56
|
handler: getIotDevices,
|
|
57
|
+
privateDocs: true,
|
|
55
58
|
summary: "List IoT devices",
|
|
56
59
|
description:
|
|
57
60
|
"Returns the IoT device status list used for admin device/order sync workflows.",
|
|
@@ -63,6 +66,7 @@ export const adminSearchRouteSpecs: RouteSpec[] = [
|
|
|
63
66
|
requestSchema: adminDevicesSchema,
|
|
64
67
|
responseSchema: adminDevicesResponseSchema,
|
|
65
68
|
handler: getDevices,
|
|
69
|
+
privateDocs: true,
|
|
66
70
|
summary: "List MongoDB devices",
|
|
67
71
|
description:
|
|
68
72
|
"Returns all device documents from MongoDB for admin sync workflows.",
|
|
@@ -159,6 +159,10 @@ const updateEntry = catchAsync(
|
|
|
159
159
|
const getEvents = catchAsync(
|
|
160
160
|
async (req: Request, res: Response): Promise<void> => {
|
|
161
161
|
const device = await devicesService.getById(req.params.deviceId);
|
|
162
|
+
if (!device) {
|
|
163
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Device not found");
|
|
164
|
+
}
|
|
165
|
+
|
|
162
166
|
const events = await iotDevicesService.getEvents({
|
|
163
167
|
...req.query,
|
|
164
168
|
createdAt: device.createdAt,
|
|
@@ -60,7 +60,7 @@ export const devicesRouteSpecs: RouteSpec[] = [
|
|
|
60
60
|
handler: devicesController.queryDevicesByUser,
|
|
61
61
|
summary: "Query devices by user",
|
|
62
62
|
description:
|
|
63
|
-
"Retrieve a paginated list of devices visible to the authenticated user.",
|
|
63
|
+
"Retrieve a paginated list of devices visible to the authenticated user. Either patient or organization is required.",
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
method: "get",
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
zUpdate,
|
|
10
10
|
zDelete,
|
|
11
11
|
zPagination,
|
|
12
|
+
zTypeFilter,
|
|
12
13
|
} from "../utils/zValidations.js";
|
|
13
14
|
|
|
14
15
|
extendZodWithOpenApi(z);
|
|
@@ -73,9 +74,9 @@ export const getEventsSchema = {
|
|
|
73
74
|
}),
|
|
74
75
|
query: z
|
|
75
76
|
.object({
|
|
76
|
-
DateStart: z.string(),
|
|
77
|
-
DateEnd: z.string(),
|
|
78
|
-
TypeFilter:
|
|
77
|
+
DateStart: z.string().datetime().optional(),
|
|
78
|
+
DateEnd: z.string().datetime().optional(),
|
|
79
|
+
TypeFilter: zTypeFilter,
|
|
79
80
|
})
|
|
80
81
|
.openapi({ description: "Fetch device events in a time range" }),
|
|
81
82
|
};
|
|
@@ -168,16 +169,28 @@ export const registerDeviceSchema = {
|
|
|
168
169
|
|
|
169
170
|
export const queryDevicesSchema = {
|
|
170
171
|
...zPagination,
|
|
171
|
-
query: zPagination.query
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
query: zPagination.query
|
|
173
|
+
.extend({
|
|
174
|
+
patient: zObjectIdFor("patient").optional().openapi({
|
|
175
|
+
description:
|
|
176
|
+
"Patient ObjectId. Required if organization is not provided.",
|
|
177
|
+
}),
|
|
178
|
+
organization: zObjectIdFor("organization").optional().openapi({
|
|
179
|
+
description:
|
|
180
|
+
"Organization ObjectId. Required if patient is not provided.",
|
|
181
|
+
}),
|
|
182
|
+
})
|
|
183
|
+
.refine((data) => data.patient || data.organization, {
|
|
184
|
+
message: "Either patient or organization is required",
|
|
185
|
+
}),
|
|
175
186
|
};
|
|
187
|
+
|
|
176
188
|
export const subscriptionSchema = {
|
|
177
189
|
params: z.object({
|
|
178
190
|
deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
|
|
179
191
|
}),
|
|
180
192
|
};
|
|
193
|
+
|
|
181
194
|
export const uploadSingleImageFromWebsiteSchema = {};
|
|
182
195
|
export const updateDeviceSchema = {
|
|
183
196
|
...zUpdate("deviceId"),
|
|
@@ -39,12 +39,17 @@ const iotDataClient = new IoTDataPlaneClient({
|
|
|
39
39
|
*/
|
|
40
40
|
export const getEvents = async (params: {
|
|
41
41
|
DeviceId: string;
|
|
42
|
-
DateStart
|
|
43
|
-
DateEnd
|
|
42
|
+
DateStart?: string;
|
|
43
|
+
DateEnd?: string;
|
|
44
44
|
TypeFilter?: string;
|
|
45
45
|
createdAt?: string;
|
|
46
46
|
}): Promise<any> => {
|
|
47
47
|
const accessToken = await getAuth0Token();
|
|
48
|
+
// Keep the requested start date, but never query events before the device existed.
|
|
49
|
+
const dateStart =
|
|
50
|
+
params.createdAt && new Date(params.DateStart) < new Date(params.createdAt)
|
|
51
|
+
? params.createdAt
|
|
52
|
+
: params.DateStart;
|
|
48
53
|
try {
|
|
49
54
|
const response: AxiosResponse = await axios.get(
|
|
50
55
|
`${process.env.IOT_API_URL}getevent`,
|
|
@@ -52,11 +57,7 @@ export const getEvents = async (params: {
|
|
|
52
57
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
53
58
|
params: {
|
|
54
59
|
DeviceId: params.DeviceId,
|
|
55
|
-
DateStart:
|
|
56
|
-
? params.DateStart
|
|
57
|
-
: params.DateStart < params.createdAt
|
|
58
|
-
? params.DateStart
|
|
59
|
-
: params.createdAt,
|
|
60
|
+
DateStart: dateStart,
|
|
60
61
|
DateEnd: params.DateEnd,
|
|
61
62
|
TypeFilter: params.TypeFilter,
|
|
62
63
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { objectId } from '../validations/custom.validation';
|
|
4
|
-
import { zGet, zObjectId, zPagination } from '../utils/zValidations';
|
|
4
|
+
import { zGet, zObjectId, zPagination, zTypeFilter } from '../utils/zValidations';
|
|
5
5
|
|
|
6
6
|
extendZodWithOpenApi(z);
|
|
7
7
|
|
|
@@ -31,9 +31,17 @@ export const getEventsSchema = {
|
|
|
31
31
|
deviceId: zObjectId.openapi({ description: 'Device ObjectId' }),
|
|
32
32
|
}),
|
|
33
33
|
query: z.object({
|
|
34
|
-
DateStart: z
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
DateStart: z
|
|
35
|
+
.string()
|
|
36
|
+
.datetime()
|
|
37
|
+
.openapi({ description: 'Start date (ISO‐string)', example: '2025-05-01T00:00:00Z' })
|
|
38
|
+
.optional(),
|
|
39
|
+
DateEnd: z
|
|
40
|
+
.string()
|
|
41
|
+
.datetime()
|
|
42
|
+
.openapi({ description: 'End date (ISO‐string)', example: '2025-05-31T23:59:59Z' })
|
|
43
|
+
.optional(),
|
|
44
|
+
TypeFilter: zTypeFilter.default(''),
|
|
37
45
|
}),
|
|
38
46
|
};
|
|
39
47
|
export const updateEntrySchema = {};
|
|
@@ -158,3 +158,11 @@ export const zDelete = (id: string) => ({
|
|
|
158
158
|
export const zObjectId = zObjectIdFor();
|
|
159
159
|
|
|
160
160
|
export const zDate = () => z.string().pipe(z.coerce.date());
|
|
161
|
+
|
|
162
|
+
export const zTypeFilter = z
|
|
163
|
+
.string()
|
|
164
|
+
.openapi({
|
|
165
|
+
description: "Event type filter. Common values include activate and state; any other string is accepted.",
|
|
166
|
+
example: "activate",
|
|
167
|
+
})
|
|
168
|
+
.optional();
|