@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,764 @@
1
+ import z, { ZodSchema } from "./Zod";
2
+ import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
3
+ import AnalyticsTableColumn from "../../Types/AnalyticsDatabase/TableColumn";
4
+ import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
5
+ import logger from "../../Server/Utils/Logger";
6
+ import { z as ZodTypes } from "zod";
7
+ import { BaseSchema, SchemaExample, ShapeRecord } from "./BaseSchema";
8
+
9
+ export type AnalyticsModelSchemaType = ZodSchema;
10
+
11
+ export class AnalyticsModelSchema extends BaseSchema {
12
+ public static getModelSchema(data: {
13
+ modelType: new () => AnalyticsBaseModel;
14
+ }): AnalyticsModelSchemaType {
15
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
16
+ const model: AnalyticsBaseModel = new modelType();
17
+
18
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
19
+
20
+ // Filter out columns with no read permissions
21
+ const filteredColumns: Array<AnalyticsTableColumn> = columns.filter(
22
+ (column: AnalyticsTableColumn) => {
23
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
24
+ if (!accessControl) {
25
+ return false;
26
+ }
27
+ const readPermissions: Array<string> = accessControl.read;
28
+ return readPermissions && readPermissions.length > 0;
29
+ },
30
+ );
31
+
32
+ const shape: ShapeRecord = {};
33
+
34
+ for (const column of filteredColumns) {
35
+ const key: string = column.key;
36
+ let zodType: ZodTypes.ZodTypeAny;
37
+
38
+ if (column.type === TableColumnType.ObjectID) {
39
+ zodType = z.string().openapi({
40
+ type: "string",
41
+ example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
42
+ });
43
+ } else if (column.type === TableColumnType.Date) {
44
+ zodType = z.date().openapi({
45
+ type: "string",
46
+ format: "date-time",
47
+ example: "2023-01-15T12:30:00.000Z",
48
+ });
49
+ } else if (column.type === TableColumnType.Text) {
50
+ zodType = z.string().openapi({
51
+ type: "string",
52
+ example: "Example text value",
53
+ });
54
+ } else if (column.type === TableColumnType.Number) {
55
+ zodType = z.number().openapi({ type: "number", example: 42 });
56
+ } else if (column.type === TableColumnType.LongNumber) {
57
+ zodType = z.number().openapi({
58
+ type: "number",
59
+ example: 1000000,
60
+ });
61
+ } else if (column.type === TableColumnType.Boolean) {
62
+ zodType = z.boolean().openapi({ type: "boolean", example: true });
63
+ } else if (column.type === TableColumnType.JSON) {
64
+ zodType = z.any().openapi({
65
+ type: "object",
66
+ example: { key: "value", nested: { data: 123 } },
67
+ });
68
+ } else if (column.type === TableColumnType.JSONArray) {
69
+ zodType = z.array(z.any()).openapi({
70
+ type: "array",
71
+ items: {
72
+ type: "object",
73
+ },
74
+ example: [{ key: "value" }, { key2: "value2" }],
75
+ });
76
+ } else if (column.type === TableColumnType.Decimal) {
77
+ zodType = z.number().openapi({
78
+ type: "number",
79
+ example: 123.45,
80
+ });
81
+ } else if (column.type === TableColumnType.ArrayNumber) {
82
+ zodType = z.array(z.number()).openapi({
83
+ type: "array",
84
+ items: {
85
+ type: "number",
86
+ },
87
+ example: [1, 2, 3, 4, 5],
88
+ });
89
+ } else if (column.type === TableColumnType.ArrayText) {
90
+ zodType = z.array(z.string()).openapi({
91
+ type: "array",
92
+ items: {
93
+ type: "string",
94
+ },
95
+ example: ["item1", "item2", "item3"],
96
+ });
97
+ } else if (
98
+ column.type === TableColumnType.NestedModel &&
99
+ column.nestedModel
100
+ ) {
101
+ // Handle nested models recursively
102
+ const nestedShape: ShapeRecord = {};
103
+ for (const nestedColumn of column.nestedModel.tableColumns) {
104
+ const nestedZodType: ZodTypes.ZodTypeAny =
105
+ this.getZodTypeForColumn(nestedColumn);
106
+ if (nestedColumn.required) {
107
+ nestedShape[nestedColumn.key] = nestedZodType;
108
+ } else {
109
+ nestedShape[nestedColumn.key] = nestedZodType.optional();
110
+ }
111
+ }
112
+ zodType = z.object(nestedShape).openapi({
113
+ type: "object",
114
+ example: this.generateNestedModelExample(
115
+ column.nestedModel.tableColumns,
116
+ ),
117
+ });
118
+ } else {
119
+ // Default fallback
120
+ zodType = z.any().openapi({
121
+ type: "string",
122
+ example: "example_value",
123
+ });
124
+ }
125
+
126
+ if (column.required) {
127
+ // leave as is
128
+ } else {
129
+ zodType = zodType.optional();
130
+ }
131
+
132
+ // Add title and description to the schema
133
+ if (column.title) {
134
+ zodType = zodType.describe(column.title);
135
+ }
136
+
137
+ shape[key] = zodType;
138
+ }
139
+
140
+ const schema: AnalyticsModelSchemaType = z.object(shape);
141
+
142
+ logger.debug(
143
+ `Analytics model schema for ${model.tableName} created with shape keys: ${Object.keys(shape).join(", ")} (filtered ${columns.length - filteredColumns.length} columns without read permissions)`,
144
+ );
145
+
146
+ return schema;
147
+ }
148
+
149
+ private static getZodTypeForColumn(
150
+ column: AnalyticsTableColumn,
151
+ ): ZodTypes.ZodTypeAny {
152
+ switch (column.type) {
153
+ case TableColumnType.ObjectID:
154
+ return z.string().openapi({
155
+ type: "string",
156
+ example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
157
+ });
158
+ case TableColumnType.Date:
159
+ return z.date().openapi({
160
+ type: "string",
161
+ format: "date-time",
162
+ example: "2023-01-15T12:30:00.000Z",
163
+ });
164
+ case TableColumnType.Text:
165
+ return z.string().openapi({
166
+ type: "string",
167
+ example: "Example text",
168
+ });
169
+ case TableColumnType.Number:
170
+ return z.number().openapi({ type: "number", example: 42 });
171
+ case TableColumnType.LongNumber:
172
+ return z.number().openapi({
173
+ type: "number",
174
+ example: 1000000,
175
+ });
176
+ case TableColumnType.Boolean:
177
+ return z.boolean().openapi({ type: "boolean", example: true });
178
+ case TableColumnType.JSON:
179
+ return z.any().openapi({
180
+ type: "object",
181
+ example: { key: "value" },
182
+ });
183
+ case TableColumnType.JSONArray:
184
+ return z.array(z.any()).openapi({
185
+ type: "array",
186
+ items: { type: "object" },
187
+ example: [{ key: "value" }],
188
+ });
189
+ case TableColumnType.Decimal:
190
+ return z.number().openapi({
191
+ type: "number",
192
+ example: 123.45,
193
+ });
194
+ case TableColumnType.ArrayNumber:
195
+ return z.array(z.number()).openapi({
196
+ type: "array",
197
+ items: { type: "number" },
198
+ example: [1, 2, 3],
199
+ });
200
+ case TableColumnType.ArrayText:
201
+ return z.array(z.string()).openapi({
202
+ type: "array",
203
+ items: { type: "string" },
204
+ example: ["item1", "item2"],
205
+ });
206
+ default:
207
+ return z.any().openapi({
208
+ type: "string",
209
+ example: "example_value",
210
+ });
211
+ }
212
+ }
213
+
214
+ private static generateNestedModelExample(
215
+ columns: Array<AnalyticsTableColumn>,
216
+ ): Record<string, unknown> {
217
+ const example: Record<string, unknown> = {};
218
+ for (const column of columns) {
219
+ switch (column.type) {
220
+ case TableColumnType.ObjectID:
221
+ example[column.key] = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
222
+ break;
223
+ case TableColumnType.Date:
224
+ example[column.key] = "2023-01-15T12:30:00.000Z";
225
+ break;
226
+ case TableColumnType.Text:
227
+ example[column.key] = "Example text";
228
+ break;
229
+ case TableColumnType.Number:
230
+ example[column.key] = 42;
231
+ break;
232
+ case TableColumnType.Boolean:
233
+ example[column.key] = true;
234
+ break;
235
+ case TableColumnType.JSON:
236
+ example[column.key] = { key: "value" };
237
+ break;
238
+ case TableColumnType.ArrayText:
239
+ example[column.key] = ["item1", "item2"];
240
+ break;
241
+ case TableColumnType.ArrayNumber:
242
+ example[column.key] = [1, 2, 3];
243
+ break;
244
+ default:
245
+ example[column.key] = "example_value";
246
+ }
247
+ }
248
+ return example;
249
+ }
250
+
251
+ public static getCreateModelSchema(data: {
252
+ modelType: new () => AnalyticsBaseModel;
253
+ }): AnalyticsModelSchemaType {
254
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
255
+ const model: AnalyticsBaseModel = new modelType();
256
+
257
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
258
+ const shape: ShapeRecord = {};
259
+
260
+ // Exclude system fields from create schema
261
+ const excludedFields: Array<string> = ["_id", "createdAt", "updatedAt"];
262
+
263
+ for (const column of columns) {
264
+ const key: string = column.key;
265
+
266
+ if (excludedFields.includes(key)) {
267
+ continue;
268
+ }
269
+
270
+ // Skip default value columns in create schema
271
+ if (column.isDefaultValueColumn) {
272
+ continue;
273
+ }
274
+
275
+ // Filter out columns with no create permissions
276
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
277
+ if (!accessControl) {
278
+ continue;
279
+ }
280
+ const createPermissions: Array<string> = accessControl.create;
281
+ if (!createPermissions || createPermissions.length === 0) {
282
+ continue;
283
+ }
284
+
285
+ const zodType: ZodTypes.ZodTypeAny = this.getZodTypeForColumn(column);
286
+
287
+ if (column.required) {
288
+ shape[key] = zodType;
289
+ } else {
290
+ shape[key] = zodType.optional();
291
+ }
292
+ }
293
+
294
+ return z.object(shape).openapi({
295
+ type: "object",
296
+ description: `Create schema for ${model.tableName || "analytics model"}`,
297
+ example: this.getCreateSchemaExample(modelType),
298
+ additionalProperties: false,
299
+ });
300
+ }
301
+
302
+ public static getQueryModelSchema(data: {
303
+ modelType: new () => AnalyticsBaseModel;
304
+ }): AnalyticsModelSchemaType {
305
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
306
+ const model: AnalyticsBaseModel = new modelType();
307
+
308
+ return this.generateQuerySchema({
309
+ model,
310
+ tableName: model.tableName || "analytics_model",
311
+ getColumns: (model: AnalyticsBaseModel) => {
312
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
313
+ return columns.map((column: AnalyticsTableColumn) => {
314
+ return { key: column.key, type: column.type };
315
+ });
316
+ },
317
+ getValidOperatorsForColumnType: (columnType: TableColumnType) => {
318
+ return this.getValidOperatorsForColumnType(columnType);
319
+ },
320
+ getOperatorSchema: (
321
+ operatorType: string,
322
+ columnType: TableColumnType,
323
+ ) => {
324
+ return this.getOperatorSchema(operatorType, columnType);
325
+ },
326
+ getQuerySchemaExample: () => {
327
+ return this.getQuerySchemaExample(modelType);
328
+ },
329
+ getExampleValueForColumn: (columnType: TableColumnType) => {
330
+ return this.getExampleValueForColumn(columnType);
331
+ },
332
+ });
333
+ }
334
+
335
+ public static getSelectModelSchema(data: {
336
+ modelType: new () => AnalyticsBaseModel;
337
+ }): AnalyticsModelSchemaType {
338
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
339
+ const model: AnalyticsBaseModel = new modelType();
340
+
341
+ return this.generateSelectSchema({
342
+ model,
343
+ tableName: model.tableName || "analytics_model",
344
+ getColumns: (model: AnalyticsBaseModel) => {
345
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
346
+ return columns.map((column: AnalyticsTableColumn) => {
347
+ return { key: column.key, type: column.type };
348
+ });
349
+ },
350
+ getSelectSchemaExample: () => {
351
+ return this.getSelectSchemaExample(modelType);
352
+ },
353
+ allowNested: false, // Analytics models typically don't have nested schemas
354
+ getNestedSchema: () => {
355
+ return null;
356
+ },
357
+ });
358
+ }
359
+
360
+ public static getSortModelSchema(data: {
361
+ modelType: new () => AnalyticsBaseModel;
362
+ }): AnalyticsModelSchemaType {
363
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
364
+ const model: AnalyticsBaseModel = new modelType();
365
+
366
+ return this.generateSortSchema({
367
+ model,
368
+ tableName: model.tableName || "analytics_model",
369
+ getSortableTypes: () => {
370
+ return this.getSortableTypes();
371
+ },
372
+ getColumnsForSorting: (model: AnalyticsBaseModel) => {
373
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
374
+ return columns.map((column: AnalyticsTableColumn) => {
375
+ return { key: column.key, type: column.type };
376
+ });
377
+ },
378
+ });
379
+ }
380
+
381
+ public static getGroupByModelSchema(data: {
382
+ modelType: new () => AnalyticsBaseModel;
383
+ }): AnalyticsModelSchemaType {
384
+ const modelType: new () => AnalyticsBaseModel = data.modelType;
385
+ const model: AnalyticsBaseModel = new modelType();
386
+
387
+ return this.generateGroupBySchema({
388
+ model,
389
+ tableName: model.tableName || "analytics_model",
390
+ getColumns: (model: AnalyticsBaseModel) => {
391
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
392
+ return columns.map((column: AnalyticsTableColumn) => {
393
+ return { key: column.key, type: column.type };
394
+ });
395
+ },
396
+ getGroupableTypes: () => {
397
+ return [
398
+ TableColumnType.Text,
399
+ TableColumnType.ObjectID,
400
+ TableColumnType.Boolean,
401
+ TableColumnType.Date,
402
+ TableColumnType.Number,
403
+ ];
404
+ },
405
+ getGroupBySchemaExample: () => {
406
+ return this.getGroupBySchemaExample(modelType);
407
+ },
408
+ });
409
+ }
410
+
411
+ private static getSortableTypes(): Array<TableColumnType> {
412
+ return [
413
+ TableColumnType.Text,
414
+ TableColumnType.Number,
415
+ TableColumnType.LongNumber,
416
+ TableColumnType.Date,
417
+ TableColumnType.Boolean,
418
+ TableColumnType.ObjectID,
419
+ TableColumnType.Decimal,
420
+ ];
421
+ }
422
+
423
+ private static getValidOperatorsForColumnType(
424
+ columnType: TableColumnType,
425
+ ): Array<string> {
426
+ switch (columnType) {
427
+ case TableColumnType.Text:
428
+ return ["EqualTo", "NotEqual", "Search", "IsNull", "NotNull"];
429
+ case TableColumnType.Number:
430
+ case TableColumnType.LongNumber:
431
+ case TableColumnType.Decimal:
432
+ return [
433
+ "EqualTo",
434
+ "NotEqual",
435
+ "GreaterThan",
436
+ "LessThan",
437
+ "GreaterThanOrEqual",
438
+ "LessThanOrEqual",
439
+ "IsNull",
440
+ "NotNull",
441
+ ];
442
+ case TableColumnType.Date:
443
+ return [
444
+ "EqualTo",
445
+ "NotEqual",
446
+ "GreaterThan",
447
+ "LessThan",
448
+ "GreaterThanOrEqual",
449
+ "LessThanOrEqual",
450
+ "IsNull",
451
+ "NotNull",
452
+ ];
453
+ case TableColumnType.Boolean:
454
+ return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
455
+ case TableColumnType.ObjectID:
456
+ return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
457
+ case TableColumnType.JSON:
458
+ case TableColumnType.JSONArray:
459
+ return ["IsNull", "NotNull"];
460
+ case TableColumnType.ArrayText:
461
+ case TableColumnType.ArrayNumber:
462
+ return ["IsNull", "NotNull"];
463
+ default:
464
+ return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
465
+ }
466
+ }
467
+
468
+ private static getExampleValueForColumn(
469
+ columnType: TableColumnType,
470
+ ): unknown {
471
+ switch (columnType) {
472
+ case TableColumnType.ObjectID:
473
+ return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
474
+ case TableColumnType.Text:
475
+ return "example text";
476
+ case TableColumnType.Number:
477
+ case TableColumnType.LongNumber:
478
+ return 42;
479
+ case TableColumnType.Decimal:
480
+ return 123.45;
481
+ case TableColumnType.Date:
482
+ return "2023-01-15T12:30:00.000Z";
483
+ case TableColumnType.Boolean:
484
+ return true;
485
+ case TableColumnType.JSON:
486
+ return { key: "value" };
487
+ case TableColumnType.JSONArray:
488
+ return [{ key: "value" }];
489
+ case TableColumnType.ArrayText:
490
+ return ["item1", "item2"];
491
+ case TableColumnType.ArrayNumber:
492
+ return [1, 2, 3];
493
+ default:
494
+ return "example_value";
495
+ }
496
+ }
497
+
498
+ private static getCreateSchemaExample(
499
+ modelType: new () => AnalyticsBaseModel,
500
+ ): SchemaExample {
501
+ const model: AnalyticsBaseModel = new modelType();
502
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
503
+ const example: SchemaExample = {};
504
+
505
+ let exampleCount: number = 0;
506
+ const maxExamples: number = 3;
507
+
508
+ for (const column of columns) {
509
+ if (exampleCount >= maxExamples) {
510
+ break;
511
+ }
512
+
513
+ // Skip system fields and default value columns
514
+ if (
515
+ ["_id", "createdAt", "updatedAt"].includes(column.key) ||
516
+ column.isDefaultValueColumn
517
+ ) {
518
+ continue;
519
+ }
520
+
521
+ // Skip columns with no create permissions
522
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
523
+ if (!accessControl) {
524
+ continue;
525
+ }
526
+ const createPermissions: Array<string> = accessControl.create;
527
+ if (!createPermissions || createPermissions.length === 0) {
528
+ continue;
529
+ }
530
+
531
+ example[column.key] = this.getExampleValueForColumn(column.type);
532
+ exampleCount++;
533
+ }
534
+
535
+ return example;
536
+ }
537
+
538
+ private static getQuerySchemaExample(
539
+ modelType: new () => AnalyticsBaseModel,
540
+ ): SchemaExample {
541
+ const model: AnalyticsBaseModel = new modelType();
542
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
543
+ const example: SchemaExample = {};
544
+
545
+ let exampleCount: number = 0;
546
+ const maxExamples: number = 2;
547
+
548
+ for (const column of columns) {
549
+ if (exampleCount >= maxExamples) {
550
+ break;
551
+ }
552
+
553
+ // Check read permissions for query operations
554
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
555
+ if (
556
+ !accessControl ||
557
+ !accessControl.read ||
558
+ accessControl.read.length === 0
559
+ ) {
560
+ continue;
561
+ }
562
+
563
+ const validOperators: Array<string> = this.getValidOperatorsForColumnType(
564
+ column.type,
565
+ );
566
+ if (validOperators.length === 0) {
567
+ continue;
568
+ }
569
+
570
+ if (column.type === TableColumnType.Text) {
571
+ example[column.key] = {
572
+ _type: "EqualTo",
573
+ value: "example text",
574
+ };
575
+ exampleCount++;
576
+ } else if (column.type === TableColumnType.Date) {
577
+ example[column.key] = {
578
+ _type: "GreaterThan",
579
+ value: "2023-01-01T00:00:00.000Z",
580
+ };
581
+ exampleCount++;
582
+ }
583
+ }
584
+
585
+ return example;
586
+ }
587
+
588
+ private static getSelectSchemaExample(
589
+ modelType: new () => AnalyticsBaseModel,
590
+ ): SchemaExample {
591
+ const model: AnalyticsBaseModel = new modelType();
592
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
593
+
594
+ // Add common fields (only if they have read permissions)
595
+ const example: SchemaExample = {};
596
+
597
+ // Check if _id has read permissions
598
+ const idAccessControl: any = model.getColumnAccessControlFor("_id");
599
+ if (
600
+ idAccessControl &&
601
+ idAccessControl.read &&
602
+ idAccessControl.read.length > 0
603
+ ) {
604
+ example["_id"] = true;
605
+ }
606
+
607
+ // Check if createdAt has read permissions
608
+ const createdAtAccessControl: any =
609
+ model.getColumnAccessControlFor("createdAt");
610
+ if (
611
+ createdAtAccessControl &&
612
+ createdAtAccessControl.read &&
613
+ createdAtAccessControl.read.length > 0
614
+ ) {
615
+ example["createdAt"] = true;
616
+ }
617
+
618
+ // Add first few non-system fields with read permissions
619
+ let fieldCount: number = 0;
620
+ const maxFields: number = 3;
621
+
622
+ for (const column of columns) {
623
+ if (fieldCount >= maxFields) {
624
+ break;
625
+ }
626
+
627
+ if (!["_id", "createdAt", "updatedAt"].includes(column.key)) {
628
+ // Check read permissions
629
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
630
+ if (
631
+ accessControl &&
632
+ accessControl.read &&
633
+ accessControl.read.length > 0
634
+ ) {
635
+ example[column.key] = true;
636
+ fieldCount++;
637
+ }
638
+ }
639
+ }
640
+
641
+ return example;
642
+ }
643
+
644
+ private static getGroupBySchemaExample(
645
+ modelType: new () => AnalyticsBaseModel,
646
+ ): SchemaExample {
647
+ const model: AnalyticsBaseModel = new modelType();
648
+ const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
649
+
650
+ // Find first suitable field for grouping with read permissions
651
+ for (const column of columns) {
652
+ const isGroupable: boolean = [
653
+ TableColumnType.Text,
654
+ TableColumnType.ObjectID,
655
+ TableColumnType.Boolean,
656
+ TableColumnType.Date,
657
+ TableColumnType.Number,
658
+ ].includes(column.type);
659
+
660
+ if (
661
+ isGroupable &&
662
+ !["_id", "createdAt", "updatedAt"].includes(column.key)
663
+ ) {
664
+ // Check read permissions
665
+ const accessControl: any = model.getColumnAccessControlFor(column.key);
666
+ if (
667
+ accessControl &&
668
+ accessControl.read &&
669
+ accessControl.read.length > 0
670
+ ) {
671
+ return { [column.key]: true };
672
+ }
673
+ }
674
+ }
675
+
676
+ // Fallback to createdAt if it has read permissions
677
+ const createdAtAccessControl: any =
678
+ model.getColumnAccessControlFor("createdAt");
679
+ if (
680
+ createdAtAccessControl &&
681
+ createdAtAccessControl.read &&
682
+ createdAtAccessControl.read.length > 0
683
+ ) {
684
+ return { ["createdAt"]: true };
685
+ }
686
+
687
+ // Final fallback - return empty object if no columns have read permissions
688
+ return {};
689
+ }
690
+
691
+ private static getOperatorSchema(
692
+ operatorType: string,
693
+ columnType: TableColumnType,
694
+ ): ZodTypes.ZodTypeAny {
695
+ const baseValue: ZodTypes.ZodTypeAny =
696
+ this.getBaseValueSchemaForColumnType(columnType);
697
+
698
+ switch (operatorType) {
699
+ case "EqualTo":
700
+ case "NotEqual":
701
+ return z.object({
702
+ _type: z.literal(operatorType),
703
+ value: baseValue,
704
+ });
705
+
706
+ case "GreaterThan":
707
+ case "LessThan":
708
+ case "GreaterThanOrEqual":
709
+ case "LessThanOrEqual":
710
+ return z.object({
711
+ _type: z.literal(operatorType),
712
+ value: baseValue,
713
+ });
714
+
715
+ case "Search":
716
+ return z.object({
717
+ _type: z.literal("Search"),
718
+ value: z.string(),
719
+ });
720
+
721
+ case "IsNull":
722
+ case "NotNull":
723
+ return z.object({
724
+ _type: z.literal(operatorType),
725
+ });
726
+
727
+ default:
728
+ return z.object({
729
+ _type: z.literal(operatorType),
730
+ value: baseValue.optional(),
731
+ });
732
+ }
733
+ }
734
+
735
+ private static getBaseValueSchemaForColumnType(
736
+ columnType: TableColumnType,
737
+ ): ZodTypes.ZodTypeAny {
738
+ switch (columnType) {
739
+ case TableColumnType.ObjectID:
740
+ case TableColumnType.Text:
741
+ return z.string();
742
+
743
+ case TableColumnType.Number:
744
+ case TableColumnType.LongNumber:
745
+ case TableColumnType.Decimal:
746
+ return z.number();
747
+
748
+ case TableColumnType.Date:
749
+ return z.date();
750
+
751
+ case TableColumnType.Boolean:
752
+ return z.boolean();
753
+
754
+ case TableColumnType.JSON:
755
+ case TableColumnType.JSONArray:
756
+ case TableColumnType.ArrayText:
757
+ case TableColumnType.ArrayNumber:
758
+ return z.any();
759
+
760
+ default:
761
+ return z.string();
762
+ }
763
+ }
764
+ }