@oneuptime/common 7.0.4358 → 7.0.4372

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 (37) hide show
  1. package/Models/AnalyticsModels/ExceptionInstance.ts +2 -2
  2. package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.ts +2 -2
  3. package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.ts +2 -2
  4. package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.ts +2 -2
  5. package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +2 -2
  6. package/Models/DatabaseModels/Probe.ts +7 -1
  7. package/Models/DatabaseModels/ServiceCatalog.ts +2 -2
  8. package/Models/DatabaseModels/ServiceCopilotCodeRepository.ts +2 -2
  9. package/Server/Utils/OpenAPI.ts +564 -2
  10. package/Utils/Schema/AnalyticsModelSchema.ts +764 -0
  11. package/Utils/Schema/BaseSchema.ts +450 -0
  12. package/Utils/Schema/ModelSchema.ts +176 -407
  13. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +2 -2
  14. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  15. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js +2 -2
  16. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js.map +1 -1
  17. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js +2 -2
  18. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js.map +1 -1
  19. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js +2 -2
  20. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js.map +1 -1
  21. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +2 -2
  22. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
  23. package/build/dist/Models/DatabaseModels/Probe.js +7 -1
  24. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  25. package/build/dist/Models/DatabaseModels/ServiceCatalog.js +2 -2
  26. package/build/dist/Models/DatabaseModels/ServiceCatalog.js.map +1 -1
  27. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +2 -2
  28. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +1 -1
  29. package/build/dist/Server/Utils/OpenAPI.js +445 -2
  30. package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
  31. package/build/dist/Utils/Schema/AnalyticsModelSchema.js +636 -0
  32. package/build/dist/Utils/Schema/AnalyticsModelSchema.js.map +1 -0
  33. package/build/dist/Utils/Schema/BaseSchema.js +295 -0
  34. package/build/dist/Utils/Schema/BaseSchema.js.map +1 -0
  35. package/build/dist/Utils/Schema/ModelSchema.js +155 -337
  36. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,450 @@
1
+ import { z as ZodTypes } from "zod";
2
+ import z, { ZodSchema } from "./Zod";
3
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
4
+ import logger from "../../Server/Utils/Logger";
5
+
6
+ export type BaseSchemaType = ZodSchema;
7
+
8
+ // Type for schema examples
9
+ export type SchemaExample = Record<string, unknown>;
10
+
11
+ // Type for shape objects using Zod's type inference
12
+ export type ShapeRecord = Record<string, ZodTypes.ZodTypeAny>;
13
+
14
+ // Type for operator examples in OpenAPI format
15
+ export type OperatorExample = {
16
+ properties: Record<
17
+ string,
18
+ {
19
+ type: string;
20
+ enum?: string[];
21
+ items?: { type: string };
22
+ }
23
+ >;
24
+ required: string[];
25
+ example: Record<string, unknown>;
26
+ };
27
+
28
+ /**
29
+ * Base class for schema generation with common functionality
30
+ * Both ModelSchema and AnalyticsModelSchema extend this class
31
+ */
32
+ export abstract class BaseSchema {
33
+ /**
34
+ * Generate a sort schema for a model
35
+ */
36
+ protected static generateSortSchema<T>(data: {
37
+ model: T;
38
+ tableName?: string;
39
+ getSortableTypes: () => Array<any>;
40
+ getColumnsForSorting: (model: T) => Array<{ key: string; type: any }>;
41
+ }): BaseSchemaType {
42
+ const shape: ShapeRecord = {};
43
+ const columns: Array<{ key: string; type: any }> =
44
+ data.getColumnsForSorting(data.model);
45
+
46
+ for (const column of columns) {
47
+ const key: string = column.key;
48
+ const isSortable: boolean = data.getSortableTypes().includes(column.type);
49
+
50
+ if (!isSortable) {
51
+ continue;
52
+ }
53
+
54
+ shape[key] = z
55
+ .enum([SortOrder.Ascending, SortOrder.Descending])
56
+ .optional()
57
+ .openapi({
58
+ type: "string",
59
+ enum: [SortOrder.Ascending, SortOrder.Descending],
60
+ description: `Sort order for ${key} field`,
61
+ example: SortOrder.Ascending,
62
+ });
63
+ }
64
+
65
+ return z.object(shape).openapi({
66
+ type: "object",
67
+ description: `Sort schema for ${data.tableName || "model"}. Only sortable fields are included.`,
68
+ example: { createdAt: SortOrder.Descending },
69
+ additionalProperties: false,
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Generate a select schema for a model
75
+ */
76
+ protected static generateSelectSchema<T>(data: {
77
+ model: T;
78
+ tableName?: string;
79
+ getColumns: (model: T) => Array<{ key: string; type?: any }>;
80
+ getSelectSchemaExample: (model: T) => SchemaExample;
81
+ allowNested?: boolean;
82
+ getNestedSchema?: (key: string, model: T) => ZodTypes.ZodTypeAny | null;
83
+ }): BaseSchemaType {
84
+ const shape: ShapeRecord = {};
85
+ const columns: Array<{ key: string; type?: any }> = data.getColumns(
86
+ data.model,
87
+ );
88
+
89
+ for (const column of columns) {
90
+ const key: string = column.key;
91
+
92
+ // Handle nested schemas if allowed and available
93
+ if (data.allowNested && data.getNestedSchema) {
94
+ const nestedSchema: ZodTypes.ZodTypeAny | null = data.getNestedSchema(
95
+ key,
96
+ data.model,
97
+ );
98
+ if (nestedSchema) {
99
+ shape[key] = nestedSchema.openapi({
100
+ type: "object",
101
+ description: `Select fields for nested ${key} entity`,
102
+ example: { id: true, name: true },
103
+ });
104
+ continue;
105
+ }
106
+ }
107
+
108
+ shape[key] = z
109
+ .boolean()
110
+ .optional()
111
+ .openapi({
112
+ type: "boolean",
113
+ description: `Select ${key} field in the response`,
114
+ example: true,
115
+ });
116
+ }
117
+
118
+ return z.object(shape).openapi({
119
+ type: "object",
120
+ description: `Select schema for ${data.tableName || "model"}. Set fields to true to include them in the response.`,
121
+ example: data.getSelectSchemaExample(data.model),
122
+ additionalProperties: false,
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Generate a group by schema for a model
128
+ */
129
+ protected static generateGroupBySchema<T>(data: {
130
+ model: T;
131
+ tableName?: string;
132
+ getColumns: (model: T) => Array<{ key: string; type: any }>;
133
+ getGroupableTypes: () => Array<any>;
134
+ getGroupBySchemaExample: (model: T) => SchemaExample;
135
+ }): BaseSchemaType {
136
+ const shape: ShapeRecord = {};
137
+ const columns: Array<{ key: string; type: any }> = data.getColumns(
138
+ data.model,
139
+ );
140
+
141
+ for (const column of columns) {
142
+ const key: string = column.key;
143
+ const isGroupable: boolean = data
144
+ .getGroupableTypes()
145
+ .includes(column.type);
146
+
147
+ if (!isGroupable) {
148
+ continue;
149
+ }
150
+
151
+ shape[key] = z
152
+ .literal(true)
153
+ .optional()
154
+ .openapi({
155
+ type: "boolean",
156
+ description: `Group by ${key} field. Only one field can be selected for grouping.`,
157
+ example: true,
158
+ });
159
+ }
160
+
161
+ return z.object(shape).openapi({
162
+ type: "object",
163
+ description: `Group by schema for ${data.tableName || "model"}. Only one field can be set to true for grouping.`,
164
+ example: data.getGroupBySchemaExample(data.model),
165
+ additionalProperties: false,
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Generate a query schema for a model
171
+ */
172
+ protected static generateQuerySchema<T>(data: {
173
+ model: T;
174
+ tableName?: string;
175
+ getColumns: (model: T) => Array<{ key: string; type: any }>;
176
+ getValidOperatorsForColumnType: (columnType: any) => Array<string>;
177
+ getOperatorSchema?: (
178
+ operatorType: string,
179
+ columnType: any,
180
+ ) => ZodTypes.ZodTypeAny;
181
+ getQuerySchemaExample: (model: T) => SchemaExample;
182
+ getExampleValueForColumn: (columnType: any) => unknown;
183
+ }): BaseSchemaType {
184
+ const shape: ShapeRecord = {};
185
+ const columns: Array<{ key: string; type: any }> = data.getColumns(
186
+ data.model,
187
+ );
188
+
189
+ for (const column of columns) {
190
+ const key: string = column.key;
191
+
192
+ // Get valid operators for this column type
193
+ const validOperators: Array<string> = data.getValidOperatorsForColumnType(
194
+ column.type,
195
+ );
196
+
197
+ if (validOperators.length === 0) {
198
+ continue;
199
+ }
200
+
201
+ let columnSchema: ZodTypes.ZodTypeAny;
202
+
203
+ if (data.getOperatorSchema) {
204
+ // Use advanced operator schemas (for regular ModelSchema)
205
+ const operatorSchemas: Array<ZodTypes.ZodTypeAny> = validOperators.map(
206
+ (operatorType: string) => {
207
+ return data.getOperatorSchema!(operatorType, column.type);
208
+ },
209
+ );
210
+
211
+ if (operatorSchemas.length === 1 && operatorSchemas[0]) {
212
+ columnSchema = operatorSchemas[0].optional();
213
+ } else if (operatorSchemas.length > 1) {
214
+ columnSchema = z
215
+ .union(
216
+ operatorSchemas as [
217
+ ZodTypes.ZodTypeAny,
218
+ ZodTypes.ZodTypeAny,
219
+ ...ZodTypes.ZodTypeAny[],
220
+ ],
221
+ )
222
+ .optional();
223
+ } else {
224
+ columnSchema = z.any().optional();
225
+ }
226
+ } else {
227
+ // Use simple operator schema (for AnalyticsModelSchema)
228
+ columnSchema = z
229
+ .object({
230
+ _type: z.enum(validOperators as [string, ...string[]]),
231
+ value: z.any().optional(),
232
+ })
233
+ .optional();
234
+ }
235
+
236
+ columnSchema = columnSchema.openapi({
237
+ type: "object",
238
+ description: `Query operators for ${key} field of type ${column.type}. Supported operators: ${validOperators.join(", ")}`,
239
+ example: {
240
+ _type: "EqualTo",
241
+ value: data.getExampleValueForColumn(column.type),
242
+ },
243
+ });
244
+
245
+ shape[key] = columnSchema;
246
+ }
247
+
248
+ return z.object(shape).openapi({
249
+ type: "object",
250
+ description: `Query schema for ${data.tableName || "model"}. Each field can use various operators based on its data type.`,
251
+ example: data.getQuerySchemaExample(data.model),
252
+ additionalProperties: false,
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Generate a create schema for a model
258
+ */
259
+ protected static generateCreateSchema<T>(data: {
260
+ model: T;
261
+ tableName?: string;
262
+ getColumns: (model: T) => Array<{
263
+ key: string;
264
+ type?: any;
265
+ required?: boolean;
266
+ isDefaultValueColumn?: boolean;
267
+ }>;
268
+ getZodTypeForColumn: (column: any) => ZodTypes.ZodTypeAny;
269
+ getCreateSchemaExample: (model: T) => SchemaExample;
270
+ excludedFields?: Array<string>;
271
+ }): BaseSchemaType {
272
+ const shape: ShapeRecord = {};
273
+ const columns: Array<{
274
+ key: string;
275
+ type?: any;
276
+ required?: boolean;
277
+ isDefaultValueColumn?: boolean;
278
+ }> = data.getColumns(data.model);
279
+ const excludedFields: Array<string> = data.excludedFields || [
280
+ "_id",
281
+ "createdAt",
282
+ "updatedAt",
283
+ ];
284
+
285
+ for (const column of columns) {
286
+ const key: string = column.key;
287
+
288
+ if (excludedFields.includes(key)) {
289
+ continue;
290
+ }
291
+
292
+ // Skip default value columns in create schema
293
+ if (column.isDefaultValueColumn) {
294
+ continue;
295
+ }
296
+
297
+ const zodType: ZodTypes.ZodTypeAny = data.getZodTypeForColumn(column);
298
+
299
+ if (column.required) {
300
+ shape[key] = zodType;
301
+ } else {
302
+ shape[key] = zodType.optional();
303
+ }
304
+ }
305
+
306
+ return z.object(shape).openapi({
307
+ type: "object",
308
+ description: `Create schema for ${data.tableName || "model"}`,
309
+ example: data.getCreateSchemaExample(data.model),
310
+ additionalProperties: false,
311
+ });
312
+ }
313
+
314
+ /**
315
+ * Helper method to generate common example values for different data types
316
+ */
317
+ protected static getCommonExampleValue(
318
+ dataType: string,
319
+ isSecondValue: boolean = false,
320
+ ): unknown {
321
+ switch (dataType.toLowerCase()) {
322
+ case "objectid":
323
+ case "id":
324
+ return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
325
+ case "text":
326
+ case "string":
327
+ return isSecondValue ? "example_text_2" : "example_text_1";
328
+ case "email":
329
+ return isSecondValue ? "user2@example.com" : "user@example.com";
330
+ case "number":
331
+ case "integer":
332
+ return isSecondValue ? 100 : 42;
333
+ case "date":
334
+ case "datetime":
335
+ return isSecondValue
336
+ ? "2023-12-31T23:59:59.000Z"
337
+ : "2023-01-15T12:30:00.000Z";
338
+ case "boolean":
339
+ return !isSecondValue;
340
+ case "json":
341
+ case "object":
342
+ return isSecondValue ? { key2: "value2" } : { key: "value" };
343
+ case "array":
344
+ return isSecondValue ? ["item3", "item4"] : ["item1", "item2"];
345
+ default:
346
+ return isSecondValue ? "example_value_2" : "example_value_1";
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Helper method to generate select schema examples
352
+ */
353
+ protected static generateSelectSchemaExample<T>(data: {
354
+ model: T;
355
+ getColumns: (model: T) => Array<{ key: string; type?: any }>;
356
+ commonFields?: Array<string>;
357
+ maxFields?: number;
358
+ priorityFieldTypes?: Array<any>;
359
+ }): SchemaExample {
360
+ const columns: Array<{ key: string; type?: any }> = data.getColumns(
361
+ data.model,
362
+ );
363
+ const example: SchemaExample = {};
364
+ const commonFields: Array<string> = data.commonFields || [
365
+ "_id",
366
+ "createdAt",
367
+ "updatedAt",
368
+ ];
369
+ const maxFields: number = data.maxFields || 5;
370
+
371
+ // Add common fields that exist
372
+ for (const field of commonFields) {
373
+ const hasField: boolean = columns.some(
374
+ (col: { key: string; type?: any }) => {
375
+ return col.key === field;
376
+ },
377
+ );
378
+ if (hasField) {
379
+ example[field] = true;
380
+ }
381
+ }
382
+
383
+ // Add priority fields if specified
384
+ let fieldCount: number = 0;
385
+ if (data.priorityFieldTypes) {
386
+ for (const column of columns) {
387
+ if (fieldCount >= maxFields) {
388
+ break;
389
+ }
390
+
391
+ if (
392
+ !commonFields.includes(column.key) &&
393
+ column.type &&
394
+ data.priorityFieldTypes.includes(column.type)
395
+ ) {
396
+ example[column.key] = true;
397
+ fieldCount++;
398
+ }
399
+ }
400
+ }
401
+
402
+ return example;
403
+ }
404
+
405
+ /**
406
+ * Helper method to generate group by schema examples
407
+ */
408
+ protected static generateGroupBySchemaExample<T>(data: {
409
+ model: T;
410
+ getColumns: (model: T) => Array<{ key: string; type: any }>;
411
+ getGroupableTypes: () => Array<any>;
412
+ excludeFields?: Array<string>;
413
+ }): SchemaExample {
414
+ const columns: Array<{ key: string; type: any }> = data.getColumns(
415
+ data.model,
416
+ );
417
+ const excludeFields: Array<string> = data.excludeFields || [
418
+ "_id",
419
+ "createdAt",
420
+ "updatedAt",
421
+ ];
422
+
423
+ // Find first suitable field for grouping
424
+ for (const column of columns) {
425
+ const isGroupable: boolean = data
426
+ .getGroupableTypes()
427
+ .includes(column.type);
428
+
429
+ if (isGroupable && !excludeFields.includes(column.key)) {
430
+ return { [column.key]: true };
431
+ }
432
+ }
433
+
434
+ // Fallback
435
+ return { createdAt: true };
436
+ }
437
+
438
+ /**
439
+ * Log schema generation debug information
440
+ */
441
+ protected static logSchemaGeneration(
442
+ schemaType: string,
443
+ tableName: string,
444
+ shape: ShapeRecord,
445
+ ): void {
446
+ logger.debug(
447
+ `${schemaType} schema for ${tableName} created with shape keys: ${Object.keys(shape).join(", ")}`,
448
+ );
449
+ }
450
+ }