@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.
@@ -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: z.string().optional(),
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.extend({
148
- patient: zObjectIdFor("patient").optional(),
149
- organization: zObjectIdFor("organization").optional(),
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: !params.createdAt
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.string().openapi({ description: 'Start date (ISO‐string)', example: '2025-05-01T00:00:00Z' }),
31
- DateEnd: z.string().openapi({ description: 'End date (ISO‐string)', example: '2025-05-31T23:59:59Z' }),
32
- TypeFilter: z.string().openapi({ description: 'Optional type filter', example: '' }).optional().default(''),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetderdinge/api",
3
- "version": "1.229.37",
3
+ "version": "1.229.40",
4
4
  "description": "Shared OpenIoT API modules",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -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: z.string().optional(),
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.extend({
172
- patient: zObjectIdFor("patient").optional(),
173
- organization: zObjectIdFor("organization").optional(),
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: string;
43
- DateEnd: string;
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: !params.createdAt
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.string().openapi({ description: 'Start date (ISO‐string)', example: '2025-05-01T00:00:00Z' }),
35
- DateEnd: z.string().openapi({ description: 'End date (ISO‐string)', example: '2025-05-31T23:59:59Z' }),
36
- TypeFilter: z.string().openapi({ description: 'Optional type filter', example: '' }).optional().default(''),
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();