@oneuptime/common 7.0.4349 → 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 (64) 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/Models/DatabaseModels/StatusPage.ts +37 -0
  10. package/Models/DatabaseModels/StatusPageSubscriber.ts +60 -0
  11. package/Server/API/StatusPageAPI.ts +104 -10
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.ts +23 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.ts +17 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  15. package/Server/Services/ScheduledMaintenanceService.ts +21 -0
  16. package/Server/Services/StatusPageSubscriberService.ts +116 -1
  17. package/Server/Utils/OpenAPI.ts +738 -11
  18. package/Server/Utils/Workspace/Slack/Slack.ts +14 -0
  19. package/Utils/Schema/AnalyticsModelSchema.ts +764 -0
  20. package/Utils/Schema/BaseSchema.ts +450 -0
  21. package/Utils/Schema/ModelSchema.ts +1099 -38
  22. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +2 -2
  23. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  24. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js +2 -2
  25. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js.map +1 -1
  26. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js +2 -2
  27. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js.map +1 -1
  28. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js +2 -2
  29. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js.map +1 -1
  30. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +2 -2
  31. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
  32. package/build/dist/Models/DatabaseModels/Probe.js +7 -1
  33. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  34. package/build/dist/Models/DatabaseModels/ServiceCatalog.js +2 -2
  35. package/build/dist/Models/DatabaseModels/ServiceCatalog.js.map +1 -1
  36. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +2 -2
  37. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +1 -1
  38. package/build/dist/Models/DatabaseModels/StatusPage.js +39 -0
  39. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  40. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +62 -0
  41. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  42. package/build/dist/Server/API/StatusPageAPI.js +73 -10
  43. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  44. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js +14 -0
  45. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js.map +1 -0
  46. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js +12 -0
  47. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js.map +1 -0
  48. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  50. package/build/dist/Server/Services/ScheduledMaintenanceService.js +19 -0
  51. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  52. package/build/dist/Server/Services/StatusPageSubscriberService.js +98 -1
  53. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  54. package/build/dist/Server/Utils/OpenAPI.js +578 -11
  55. package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
  56. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +15 -0
  57. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  58. package/build/dist/Utils/Schema/AnalyticsModelSchema.js +636 -0
  59. package/build/dist/Utils/Schema/AnalyticsModelSchema.js.map +1 -0
  60. package/build/dist/Utils/Schema/BaseSchema.js +295 -0
  61. package/build/dist/Utils/Schema/BaseSchema.js.map +1 -0
  62. package/build/dist/Utils/Schema/ModelSchema.js +940 -27
  63. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  64. package/package.json +1 -1
@@ -1,20 +1,56 @@
1
1
  import z from "./Zod";
2
2
  import TableColumnType from "../../Types/Database/TableColumnType";
3
3
  import { getTableColumns, } from "../../Types/Database/TableColumn";
4
- import SortOrder from "../../Types/BaseDatabase/SortOrder";
5
4
  import logger from "../../Server/Utils/Logger";
6
5
  import Color from "../../Types/Color";
7
- export class ModelSchema {
6
+ import BadDataException from "../../Types/Exception/BadDataException";
7
+ import { PermissionHelper } from "../../Types/Permission";
8
+ import { BaseSchema } from "./BaseSchema";
9
+ export class ModelSchema extends BaseSchema {
10
+ /**
11
+ * Format permissions array into a human-readable string for OpenAPI documentation
12
+ */
13
+ static formatPermissionsForSchema(permissions) {
14
+ if (!permissions || permissions.length === 0) {
15
+ return "No access - you don't have permission for this operation";
16
+ }
17
+ return PermissionHelper.getPermissionTitles(permissions).join(", ");
18
+ }
19
+ /**
20
+ * Get permissions description for a column to add to OpenAPI schema
21
+ */
22
+ static getColumnPermissionsDescription(model, key) {
23
+ const accessControl = model.getColumnAccessControlForAllColumns()[key];
24
+ if (!accessControl) {
25
+ return "";
26
+ }
27
+ const createPermissions = this.formatPermissionsForSchema(accessControl.create);
28
+ const readPermissions = this.formatPermissionsForSchema(accessControl.read);
29
+ const updatePermissions = this.formatPermissionsForSchema(accessControl.update);
30
+ return `Permissions - Create: [${createPermissions}], Read: [${readPermissions}], Update: [${updatePermissions}]`;
31
+ }
8
32
  static getModelSchema(data) {
9
33
  const modelType = data.modelType;
10
34
  const model = new modelType();
11
35
  const columns = getTableColumns(model);
12
36
  const shape = {};
37
+ // Get column access control for permission filtering
38
+ const columnAccessControl = model.getColumnAccessControlForAllColumns();
13
39
  for (const key in columns) {
14
40
  const column = columns[key];
15
41
  if (!column) {
16
42
  continue;
17
43
  }
44
+ // Filter out columns with no permissions (root-only access)
45
+ const accessControl = columnAccessControl[key];
46
+ if (accessControl) {
47
+ // Check if column has any permissions defined for read operation (general schema assumes read access)
48
+ const hasReadPermissions = accessControl.read && accessControl.read.length > 0;
49
+ // If no read permissions are defined, exclude the column from general schema
50
+ if (!hasReadPermissions) {
51
+ continue;
52
+ }
53
+ }
18
54
  let zodType;
19
55
  if (column.type === TableColumnType.ObjectID) {
20
56
  zodType = z.string().openapi({
@@ -280,6 +316,11 @@ export class ModelSchema {
280
316
  if (column.title) {
281
317
  zodType = zodType.describe(column.title);
282
318
  }
319
+ // Add permissions description to the schema
320
+ const permissionsDescription = this.getColumnPermissionsDescription(model, key);
321
+ if (permissionsDescription) {
322
+ zodType = zodType.describe(permissionsDescription);
323
+ }
283
324
  shape[key] = zodType;
284
325
  }
285
326
  const schema = z.object(shape);
@@ -301,63 +342,935 @@ export class ModelSchema {
301
342
  }
302
343
  static getQueryModelSchema(data) {
303
344
  const modelType = data.modelType;
345
+ const model = new modelType();
346
+ return this.generateQuerySchema({
347
+ model,
348
+ tableName: model.tableName || "model",
349
+ getColumns: (model) => {
350
+ const columns = getTableColumns(model);
351
+ return Object.keys(columns)
352
+ .map((key) => {
353
+ const column = columns[key];
354
+ return column ? { key, type: column.type } : null;
355
+ })
356
+ .filter((col) => {
357
+ return col !== null;
358
+ });
359
+ },
360
+ getValidOperatorsForColumnType: (columnType) => {
361
+ return this.getValidOperatorsForColumnType(columnType);
362
+ },
363
+ getOperatorSchema: (operatorType, columnType) => {
364
+ return this.getOperatorSchema(operatorType, columnType);
365
+ },
366
+ getQuerySchemaExample: () => {
367
+ return this.getQuerySchemaExample(modelType);
368
+ },
369
+ getExampleValueForColumn: (columnType) => {
370
+ return this.getExampleValueForColumn(columnType);
371
+ },
372
+ });
373
+ }
374
+ static getValidOperatorsForColumnType(columnType) {
375
+ const commonOperators = [
376
+ "EqualTo",
377
+ "NotEqual",
378
+ "IsNull",
379
+ "NotNull",
380
+ "EqualToOrNull",
381
+ ];
382
+ switch (columnType) {
383
+ case TableColumnType.ObjectID:
384
+ case TableColumnType.Email:
385
+ case TableColumnType.Phone:
386
+ case TableColumnType.HashedString:
387
+ case TableColumnType.Slug:
388
+ return [...commonOperators, "Includes"];
389
+ case TableColumnType.ShortText:
390
+ case TableColumnType.LongText:
391
+ case TableColumnType.VeryLongText:
392
+ case TableColumnType.Name:
393
+ case TableColumnType.Description:
394
+ case TableColumnType.Domain:
395
+ case TableColumnType.Markdown:
396
+ return [...commonOperators, "Search", "Includes"];
397
+ case TableColumnType.Number:
398
+ case TableColumnType.PositiveNumber:
399
+ case TableColumnType.SmallNumber:
400
+ case TableColumnType.SmallPositiveNumber:
401
+ case TableColumnType.BigNumber:
402
+ case TableColumnType.BigPositiveNumber:
403
+ return [
404
+ ...commonOperators,
405
+ "GreaterThan",
406
+ "LessThan",
407
+ "GreaterThanOrEqual",
408
+ "LessThanOrEqual",
409
+ "InBetween",
410
+ ];
411
+ case TableColumnType.Date:
412
+ return [
413
+ ...commonOperators,
414
+ "GreaterThan",
415
+ "LessThan",
416
+ "GreaterThanOrEqual",
417
+ "LessThanOrEqual",
418
+ "InBetween",
419
+ ];
420
+ case TableColumnType.Boolean:
421
+ return commonOperators;
422
+ case TableColumnType.JSON:
423
+ case TableColumnType.Array:
424
+ return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
425
+ case TableColumnType.Entity:
426
+ case TableColumnType.EntityArray:
427
+ return ["EqualTo", "NotEqual", "IsNull", "NotNull", "Includes"];
428
+ case TableColumnType.Color:
429
+ case TableColumnType.File:
430
+ case TableColumnType.Buffer:
431
+ return commonOperators;
432
+ default:
433
+ return commonOperators;
434
+ }
435
+ }
436
+ static getOperatorSchema(operatorType, columnType) {
437
+ const baseValue = this.getBaseValueSchemaForColumnType(columnType);
438
+ switch (operatorType) {
439
+ case "EqualTo":
440
+ case "NotEqual":
441
+ case "EqualToOrNull":
442
+ return z.object({
443
+ _type: z.literal(operatorType),
444
+ value: baseValue,
445
+ });
446
+ case "GreaterThan":
447
+ case "LessThan":
448
+ case "GreaterThanOrEqual":
449
+ case "LessThanOrEqual":
450
+ return z.object({
451
+ _type: z.literal(operatorType),
452
+ value: baseValue,
453
+ });
454
+ case "Search":
455
+ return z.object({
456
+ _type: z.literal("Search"),
457
+ value: z.string(),
458
+ });
459
+ case "InBetween":
460
+ return z.object({
461
+ _type: z.literal("InBetween"),
462
+ startValue: baseValue,
463
+ endValue: baseValue,
464
+ });
465
+ case "IsNull":
466
+ case "NotNull":
467
+ return z.object({
468
+ _type: z.literal(operatorType),
469
+ });
470
+ case "Includes":
471
+ return z.object({
472
+ _type: z.literal("Includes"),
473
+ value: z.array(baseValue),
474
+ });
475
+ default:
476
+ return z.object({
477
+ _type: z.literal(operatorType),
478
+ value: baseValue,
479
+ });
480
+ }
481
+ }
482
+ static getBaseValueSchemaForColumnType(columnType) {
483
+ switch (columnType) {
484
+ case TableColumnType.ObjectID:
485
+ return z.string();
486
+ case TableColumnType.Email:
487
+ return z.string().email();
488
+ case TableColumnType.Phone:
489
+ case TableColumnType.HashedString:
490
+ case TableColumnType.Slug:
491
+ case TableColumnType.ShortText:
492
+ case TableColumnType.LongText:
493
+ case TableColumnType.VeryLongText:
494
+ case TableColumnType.Name:
495
+ case TableColumnType.Description:
496
+ case TableColumnType.Domain:
497
+ case TableColumnType.Markdown:
498
+ case TableColumnType.HTML:
499
+ case TableColumnType.JavaScript:
500
+ case TableColumnType.CSS:
501
+ case TableColumnType.LongURL:
502
+ case TableColumnType.ShortURL:
503
+ case TableColumnType.OTP:
504
+ case TableColumnType.Password:
505
+ case TableColumnType.Version:
506
+ return z.string();
507
+ case TableColumnType.Number:
508
+ case TableColumnType.PositiveNumber:
509
+ case TableColumnType.SmallNumber:
510
+ case TableColumnType.SmallPositiveNumber:
511
+ case TableColumnType.BigNumber:
512
+ case TableColumnType.BigPositiveNumber:
513
+ return z.number();
514
+ case TableColumnType.Date:
515
+ return z.date();
516
+ case TableColumnType.Boolean:
517
+ return z.boolean();
518
+ case TableColumnType.JSON:
519
+ case TableColumnType.Array:
520
+ case TableColumnType.Permission:
521
+ case TableColumnType.CustomFieldType:
522
+ return z.any();
523
+ case TableColumnType.Color:
524
+ return Color.getSchema();
525
+ case TableColumnType.Entity:
526
+ return z.string(); // Entity IDs are typically strings
527
+ case TableColumnType.EntityArray:
528
+ return z.array(z.string()); // Array of entity IDs
529
+ case TableColumnType.File:
530
+ case TableColumnType.Buffer:
531
+ return z.any();
532
+ case TableColumnType.MonitorType:
533
+ case TableColumnType.WorkflowStatus:
534
+ return z.string();
535
+ default:
536
+ return z.string();
537
+ }
538
+ }
539
+ static getSortModelSchema(data) {
540
+ const modelType = data.modelType;
541
+ const model = new modelType();
542
+ return this.generateSortSchema({
543
+ model,
544
+ tableName: model.tableName || "model",
545
+ getSortableTypes: () => {
546
+ return this.getSortableTypes();
547
+ },
548
+ getColumnsForSorting: (model) => {
549
+ const columns = getTableColumns(model);
550
+ return Object.keys(columns)
551
+ .map((key) => {
552
+ const column = columns[key];
553
+ return column ? { key, type: column.type } : null;
554
+ })
555
+ .filter((col) => {
556
+ return col !== null;
557
+ });
558
+ },
559
+ });
560
+ }
561
+ static getSelectModelSchema(data) {
562
+ const modelType = data.modelType;
563
+ const model = new modelType();
564
+ return this.generateSelectSchema({
565
+ model,
566
+ tableName: model.tableName || "model",
567
+ getColumns: (model) => {
568
+ const columns = getTableColumns(model);
569
+ return Object.keys(columns)
570
+ .map((key) => {
571
+ const column = columns[key];
572
+ return column ? { key, type: column.type } : null;
573
+ })
574
+ .filter((col) => {
575
+ return col !== null;
576
+ });
577
+ },
578
+ getSelectSchemaExample: () => {
579
+ return this.getSelectSchemaExample(modelType);
580
+ },
581
+ allowNested: !data.isNested,
582
+ getNestedSchema: (key, model) => {
583
+ const columns = getTableColumns(model);
584
+ const column = columns[key];
585
+ if (column &&
586
+ column.modelType &&
587
+ (column.type === TableColumnType.EntityArray ||
588
+ column.type === TableColumnType.Entity)) {
589
+ return this.getSelectModelSchema({
590
+ modelType: column.modelType,
591
+ isNested: true,
592
+ });
593
+ }
594
+ return null;
595
+ },
596
+ });
597
+ }
598
+ static getGroupByModelSchema(data) {
599
+ const modelType = data.modelType;
600
+ const model = new modelType();
601
+ return this.generateGroupBySchema({
602
+ model,
603
+ tableName: model.tableName || "model",
604
+ getColumns: (model) => {
605
+ const columns = getTableColumns(model);
606
+ return Object.keys(columns)
607
+ .map((key) => {
608
+ const column = columns[key];
609
+ return column ? { key, type: column.type } : null;
610
+ })
611
+ .filter((col) => {
612
+ return col !== null;
613
+ });
614
+ },
615
+ getGroupableTypes: () => {
616
+ return [
617
+ TableColumnType.ShortText,
618
+ TableColumnType.LongText,
619
+ TableColumnType.Name,
620
+ TableColumnType.Email,
621
+ TableColumnType.Slug,
622
+ TableColumnType.ObjectID,
623
+ TableColumnType.Boolean,
624
+ TableColumnType.Date,
625
+ TableColumnType.Number,
626
+ TableColumnType.PositiveNumber,
627
+ TableColumnType.SmallNumber,
628
+ TableColumnType.SmallPositiveNumber,
629
+ TableColumnType.BigNumber,
630
+ TableColumnType.BigPositiveNumber,
631
+ ];
632
+ },
633
+ getGroupBySchemaExample: () => {
634
+ return this.getGroupBySchemaExample(modelType);
635
+ },
636
+ });
637
+ }
638
+ static getExampleValueForColumn(columnType, isSecondValue = false) {
639
+ switch (columnType) {
640
+ case TableColumnType.ObjectID:
641
+ return isSecondValue
642
+ ? "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
643
+ : "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
644
+ case TableColumnType.Email:
645
+ return isSecondValue ? "jane@example.com" : "john@example.com";
646
+ case TableColumnType.Phone:
647
+ return isSecondValue ? "+1-555-987-6543" : "+1-555-123-4567";
648
+ case TableColumnType.Number:
649
+ case TableColumnType.PositiveNumber:
650
+ return isSecondValue ? 100 : 50;
651
+ case TableColumnType.SmallNumber:
652
+ return isSecondValue ? 20 : 10;
653
+ case TableColumnType.SmallPositiveNumber:
654
+ return isSecondValue ? 25 : 15;
655
+ case TableColumnType.BigNumber:
656
+ return isSecondValue ? 2000000 : 1000000;
657
+ case TableColumnType.BigPositiveNumber:
658
+ return isSecondValue ? 2500000 : 1500000;
659
+ case TableColumnType.Date:
660
+ return isSecondValue
661
+ ? "2023-12-31T23:59:59.000Z"
662
+ : "2023-01-01T00:00:00.000Z";
663
+ case TableColumnType.Boolean:
664
+ return !isSecondValue;
665
+ case TableColumnType.ShortText:
666
+ case TableColumnType.Name:
667
+ return isSecondValue ? "Jane Doe" : "John Doe";
668
+ case TableColumnType.Description:
669
+ return isSecondValue
670
+ ? "Second example description"
671
+ : "Example description";
672
+ case TableColumnType.Domain:
673
+ return isSecondValue ? "example.org" : "example.com";
674
+ default:
675
+ return isSecondValue ? "example_value_2" : "example_value_1";
676
+ }
677
+ }
678
+ static getQuerySchemaExample(modelType) {
304
679
  const model = new modelType();
305
680
  const columns = getTableColumns(model);
306
- const shape = {};
681
+ const example = {};
682
+ let exampleCount = 0;
683
+ const maxExamples = 3;
307
684
  for (const key in columns) {
685
+ if (exampleCount >= maxExamples) {
686
+ break;
687
+ }
308
688
  const column = columns[key];
309
689
  if (!column) {
310
690
  continue;
311
691
  }
312
- // There are these query operators.
692
+ const validOperators = this.getValidOperatorsForColumnType(column.type);
693
+ if (validOperators.length === 0) {
694
+ continue;
695
+ }
696
+ // Add example based on column type and available operators
697
+ if (column.type === TableColumnType.ShortText ||
698
+ column.type === TableColumnType.Name) {
699
+ if (validOperators.includes("EqualTo")) {
700
+ example[key] = { _type: "EqualTo", value: "Example Text" };
701
+ exampleCount++;
702
+ }
703
+ else if (validOperators.includes("Search")) {
704
+ example[key] = { _type: "Search", value: "example" };
705
+ exampleCount++;
706
+ }
707
+ }
708
+ else if (column.type === TableColumnType.Email &&
709
+ validOperators.includes("EqualTo")) {
710
+ example[key] = { _type: "EqualTo", value: "user@example.com" };
711
+ exampleCount++;
712
+ }
713
+ else if (column.type === TableColumnType.Date &&
714
+ validOperators.includes("GreaterThan")) {
715
+ example[key] = {
716
+ _type: "GreaterThan",
717
+ value: "2023-01-01T00:00:00.000Z",
718
+ };
719
+ exampleCount++;
720
+ }
721
+ else if (column.type === TableColumnType.Boolean &&
722
+ validOperators.includes("EqualTo")) {
723
+ example[key] = { _type: "EqualTo", value: true };
724
+ exampleCount++;
725
+ }
726
+ else if ((column.type === TableColumnType.Number ||
727
+ column.type === TableColumnType.PositiveNumber) &&
728
+ validOperators.includes("GreaterThan")) {
729
+ example[key] = { _type: "GreaterThan", value: 10 };
730
+ exampleCount++;
731
+ }
732
+ else if (validOperators.includes("EqualTo")) {
733
+ example[key] = {
734
+ _type: "EqualTo",
735
+ value: this.getExampleValueForColumn(column.type),
736
+ };
737
+ exampleCount++;
738
+ }
739
+ }
740
+ // If no examples were added, add a generic one
741
+ if (exampleCount === 0) {
742
+ example["_id"] = {
743
+ _type: "EqualTo",
744
+ value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
745
+ };
746
+ }
747
+ return example;
748
+ }
749
+ static getSelectSchemaExample(modelType) {
750
+ if (!modelType) {
751
+ throw new BadDataException("Model type is required to generate select schema example.");
752
+ }
753
+ const model = new modelType();
754
+ const columns = getTableColumns(model);
755
+ const example = {};
756
+ // Add common fields that most models have
757
+ const commonFields = ["_id", "createdAt", "updatedAt"];
758
+ for (const field of commonFields) {
759
+ if (columns[field]) {
760
+ example[field] = true;
761
+ }
762
+ }
763
+ // Add first few non-common fields as examples
764
+ let fieldCount = 0;
765
+ const maxFields = 5;
766
+ for (const key in columns) {
767
+ if (fieldCount >= maxFields) {
768
+ break;
769
+ }
770
+ const column = columns[key];
771
+ if (!column || commonFields.includes(key)) {
772
+ continue;
773
+ }
774
+ // Prioritize fields that are likely to be commonly selected
775
+ if (column.type === TableColumnType.ShortText ||
776
+ column.type === TableColumnType.Name ||
777
+ column.type === TableColumnType.Email ||
778
+ column.type === TableColumnType.Description) {
779
+ example[key] = true;
780
+ fieldCount++;
781
+ }
313
782
  }
314
- return z.object(shape);
783
+ return example;
315
784
  }
316
- static getSortModelSchema(data) {
317
- const modelType = data.modelType;
785
+ static getGroupBySchemaExample(modelType) {
786
+ if (!modelType) {
787
+ throw new BadDataException("Model type is required to generate group by schema example.");
788
+ }
318
789
  const model = new modelType();
319
790
  const columns = getTableColumns(model);
320
- const shape = {};
791
+ // Find the first suitable field for grouping
321
792
  for (const key in columns) {
322
793
  const column = columns[key];
323
794
  if (!column) {
324
795
  continue;
325
796
  }
326
- const isSortable = ModelSchema.getSortableTypes().includes(column.type);
327
- if (!isSortable) {
797
+ // Prioritize common groupable fields
798
+ if (column.type === TableColumnType.ShortText ||
799
+ column.type === TableColumnType.Name ||
800
+ column.type === TableColumnType.Boolean ||
801
+ column.type === TableColumnType.Date) {
802
+ return { [key]: true };
803
+ }
804
+ }
805
+ // Fallback to any available field
806
+ for (const key in columns) {
807
+ const column = columns[key];
808
+ if (!column) {
328
809
  continue;
329
810
  }
330
- shape[key] = z
331
- .enum([SortOrder.Ascending, SortOrder.Descending])
332
- .optional();
811
+ const isGroupable = [
812
+ TableColumnType.ShortText,
813
+ TableColumnType.LongText,
814
+ TableColumnType.Name,
815
+ TableColumnType.Email,
816
+ TableColumnType.Slug,
817
+ TableColumnType.ObjectID,
818
+ TableColumnType.Boolean,
819
+ TableColumnType.Date,
820
+ TableColumnType.Number,
821
+ TableColumnType.PositiveNumber,
822
+ TableColumnType.SmallNumber,
823
+ TableColumnType.SmallPositiveNumber,
824
+ TableColumnType.BigNumber,
825
+ TableColumnType.BigPositiveNumber,
826
+ ].includes(column.type);
827
+ if (isGroupable) {
828
+ return { [key]: true };
829
+ }
333
830
  }
334
- return z.object(shape);
831
+ // Final fallback
832
+ return { status: true };
335
833
  }
336
- static getSelectModelSchema(data) {
834
+ // Shared method to build model schemas with different field exclusions
835
+ static buildModelSchema(data) {
337
836
  const modelType = data.modelType;
338
837
  const model = new modelType();
339
838
  const columns = getTableColumns(model);
340
839
  const shape = {};
840
+ // Get column access control for permission filtering
841
+ const columnAccessControl = model.getColumnAccessControlForAllColumns();
341
842
  for (const key in columns) {
342
843
  const column = columns[key];
343
844
  if (!column) {
344
845
  continue;
345
846
  }
346
- // if its entity array or entity then you can select nested properties
347
- if (!data.isNested &&
348
- column.modelType &&
349
- (column.type === TableColumnType.EntityArray ||
350
- column.type === TableColumnType.Entity)) {
351
- // can only do one level of nesting
352
- shape[key] = this.getSelectModelSchema({
353
- modelType: column.modelType,
354
- isNested: true,
355
- });
847
+ // Skip excluded fields
848
+ if (data.excludedFields && data.excludedFields.includes(key)) {
356
849
  continue;
357
850
  }
358
- shape[key] = z.boolean().optional();
851
+ // Only include specified fields if includedFields is provided
852
+ if (data.includedFields && !data.includedFields.includes(key)) {
853
+ continue;
854
+ }
855
+ // Filter out columns with no permissions (root-only access)
856
+ const accessControl = columnAccessControl[key];
857
+ if (accessControl) {
858
+ let hasPermissions = false;
859
+ // Check if column has any permissions defined for the current operation
860
+ if (data.schemaType === "create" &&
861
+ accessControl.create &&
862
+ accessControl.create.length > 0) {
863
+ hasPermissions = true;
864
+ }
865
+ else if (data.schemaType === "read" &&
866
+ accessControl.read &&
867
+ accessControl.read.length > 0) {
868
+ hasPermissions = true;
869
+ }
870
+ else if (data.schemaType === "update" &&
871
+ accessControl.update &&
872
+ accessControl.update.length > 0) {
873
+ hasPermissions = true;
874
+ }
875
+ else if (data.schemaType === "delete") {
876
+ // For delete operations, we don't filter by column permissions
877
+ hasPermissions = true;
878
+ }
879
+ // If no permissions are defined for this operation, exclude the column
880
+ if (!hasPermissions) {
881
+ continue;
882
+ }
883
+ }
884
+ let zodType = this.getZodTypeForColumn(column, key, data.schemaType);
885
+ // Make fields optional if specified
886
+ if (data.makeOptional) {
887
+ zodType = zodType.optional();
888
+ }
889
+ // Add title and description to the schema
890
+ if (column.title) {
891
+ zodType = zodType.describe(column.title);
892
+ }
893
+ shape[key] = zodType;
894
+ }
895
+ const schema = z.object(shape).openapi({
896
+ type: "object",
897
+ description: `${data.description} schema for ${model.tableName || "model"} model. ${data.description}`,
898
+ example: data.example,
899
+ additionalProperties: false,
900
+ });
901
+ logger.debug(`${data.schemaType} model schema for ${model.tableName} created with shape: ${JSON.stringify(shape, null, 2)}`);
902
+ return schema;
903
+ }
904
+ // Helper method to get Zod type for a column
905
+ static getZodTypeForColumn(column, key, schemaType) {
906
+ let zodType;
907
+ if (column.type === TableColumnType.ObjectID) {
908
+ zodType = z.string().openapi({
909
+ type: "string",
910
+ example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
911
+ });
912
+ }
913
+ else if (column.type === TableColumnType.Color) {
914
+ zodType = Color.getSchema();
915
+ }
916
+ else if (column.type === TableColumnType.Date) {
917
+ zodType = z.date().openapi({
918
+ type: "string",
919
+ format: "date-time",
920
+ example: "2023-01-15T12:30:00.000Z",
921
+ });
922
+ }
923
+ else if (column.type === TableColumnType.VeryLongText) {
924
+ zodType = z.string().openapi({
925
+ type: "string",
926
+ example: "This is an example of very long text content that might be stored in this field. It can contain a lot of information, such as detailed descriptions, comments, or any other lengthy text data that needs to be stored in the database.",
927
+ });
928
+ }
929
+ else if (column.type === TableColumnType.Number ||
930
+ column.type === TableColumnType.PositiveNumber) {
931
+ zodType = z.number().openapi({ type: "number", example: 42 });
932
+ }
933
+ else if (column.type === TableColumnType.Email) {
934
+ zodType = z.string().email().openapi({
935
+ type: "string",
936
+ format: "email",
937
+ example: "user@example.com",
938
+ });
939
+ }
940
+ else if (column.type === TableColumnType.HashedString) {
941
+ zodType = z
942
+ .string()
943
+ .openapi({ type: "string", example: "hashed_string_value" });
944
+ }
945
+ else if (column.type === TableColumnType.Slug) {
946
+ zodType = z
947
+ .string()
948
+ .openapi({ type: "string", example: "example-slug-value" });
949
+ }
950
+ else if (column.type === TableColumnType.ShortText) {
951
+ zodType = z
952
+ .string()
953
+ .openapi({ type: "string", example: "Example short text" });
954
+ }
955
+ else if (column.type === TableColumnType.LongText) {
956
+ zodType = z.string().openapi({
957
+ type: "string",
958
+ example: "This is an example of longer text content that might be stored in this field.",
959
+ });
960
+ }
961
+ else if (column.type === TableColumnType.Phone) {
962
+ zodType = z
963
+ .string()
964
+ .openapi({ type: "string", example: "+1-555-123-4567" });
965
+ }
966
+ else if (column.type === TableColumnType.Version) {
967
+ zodType = z.string().openapi({
968
+ type: "string",
969
+ example: "1.0.0",
970
+ });
971
+ }
972
+ else if (column.type === TableColumnType.Password) {
973
+ zodType = z.string().openapi({
974
+ type: "string",
975
+ format: "password",
976
+ example: "••••••••",
977
+ });
978
+ }
979
+ else if (column.type === TableColumnType.Name) {
980
+ zodType = z.string().openapi({
981
+ type: "string",
982
+ example: "John Doe",
983
+ });
984
+ }
985
+ else if (column.type === TableColumnType.Description) {
986
+ zodType = z.string().openapi({
987
+ type: "string",
988
+ example: "This is a description of the item",
989
+ });
990
+ }
991
+ else if (column.type === TableColumnType.File) {
992
+ zodType = z.any().openapi({
993
+ type: "string",
994
+ format: "binary",
995
+ });
996
+ }
997
+ else if (column.type === TableColumnType.Buffer) {
998
+ zodType = z.any().openapi({
999
+ type: "string",
1000
+ format: "binary",
1001
+ });
1002
+ }
1003
+ else if (column.type === TableColumnType.ShortURL) {
1004
+ zodType = z.string().url().openapi({
1005
+ type: "string",
1006
+ example: "https://short.url/abc123",
1007
+ });
359
1008
  }
360
- return z.object(shape);
1009
+ else if (column.type === TableColumnType.Markdown) {
1010
+ zodType = z.string().openapi({
1011
+ type: "string",
1012
+ example: "# Heading\n\nThis is **markdown** content",
1013
+ });
1014
+ }
1015
+ else if (column.type === TableColumnType.Domain) {
1016
+ zodType = z.string().openapi({
1017
+ type: "string",
1018
+ example: "example.com",
1019
+ });
1020
+ }
1021
+ else if (column.type === TableColumnType.LongURL) {
1022
+ zodType = z.string().url().openapi({
1023
+ type: "string",
1024
+ example: "https://www.example.com/path/to/resource?param=value",
1025
+ });
1026
+ }
1027
+ else if (column.type === TableColumnType.OTP) {
1028
+ zodType = z.string().openapi({
1029
+ type: "string",
1030
+ example: "123456",
1031
+ });
1032
+ }
1033
+ else if (column.type === TableColumnType.HTML) {
1034
+ zodType = z.string().openapi({
1035
+ type: "string",
1036
+ example: "<div><h1>Title</h1><p>Content</p></div>",
1037
+ });
1038
+ }
1039
+ else if (column.type === TableColumnType.JavaScript) {
1040
+ zodType = z.string().openapi({
1041
+ type: "string",
1042
+ example: "function example() { return true; }",
1043
+ });
1044
+ }
1045
+ else if (column.type === TableColumnType.CSS) {
1046
+ zodType = z.string().openapi({
1047
+ type: "string",
1048
+ example: "body { color: #333; margin: 0; }",
1049
+ });
1050
+ }
1051
+ else if (column.type === TableColumnType.Array) {
1052
+ zodType = z.array(z.any()).openapi({
1053
+ type: "array",
1054
+ items: {
1055
+ type: "string",
1056
+ },
1057
+ example: ["item1", "item2", "item3"],
1058
+ });
1059
+ }
1060
+ else if (column.type === TableColumnType.SmallPositiveNumber) {
1061
+ zodType = z.number().int().nonnegative().openapi({
1062
+ type: "integer",
1063
+ example: 5,
1064
+ });
1065
+ }
1066
+ else if (column.type === TableColumnType.BigPositiveNumber) {
1067
+ zodType = z.number().nonnegative().openapi({
1068
+ type: "number",
1069
+ example: 1000000,
1070
+ });
1071
+ }
1072
+ else if (column.type === TableColumnType.SmallNumber) {
1073
+ zodType = z.number().int().openapi({
1074
+ type: "integer",
1075
+ example: 10,
1076
+ });
1077
+ }
1078
+ else if (column.type === TableColumnType.BigNumber) {
1079
+ zodType = z.number().openapi({
1080
+ type: "number",
1081
+ example: 1000000,
1082
+ });
1083
+ }
1084
+ else if (column.type === TableColumnType.Permission) {
1085
+ zodType = z.any().openapi({
1086
+ type: "object",
1087
+ example: { read: true, write: false, delete: false },
1088
+ });
1089
+ }
1090
+ else if (column.type === TableColumnType.CustomFieldType) {
1091
+ zodType = z.any().openapi({
1092
+ type: "object",
1093
+ example: { type: "text", required: true },
1094
+ });
1095
+ }
1096
+ else if (column.type === TableColumnType.MonitorType) {
1097
+ zodType = z.string().openapi({
1098
+ type: "string",
1099
+ example: "HTTP",
1100
+ });
1101
+ }
1102
+ else if (column.type === TableColumnType.WorkflowStatus) {
1103
+ zodType = z.string().openapi({
1104
+ type: "string",
1105
+ example: "In Progress",
1106
+ });
1107
+ }
1108
+ else if (column.type === TableColumnType.Boolean) {
1109
+ zodType = z.boolean().openapi({ type: "boolean", example: true });
1110
+ }
1111
+ else if (column.type === TableColumnType.JSON) {
1112
+ zodType = z.any().openapi({
1113
+ type: "object",
1114
+ example: { key: "value", nested: { data: 123 } },
1115
+ });
1116
+ }
1117
+ else if (column.type === TableColumnType.EntityArray) {
1118
+ const entityArrayType = column.modelType;
1119
+ if (!entityArrayType) {
1120
+ logger.debug(`Entity type is not defined for column ${key}`);
1121
+ return z.any().openapi({ type: "null", example: null });
1122
+ }
1123
+ // Use the appropriate schema method based on the operation type
1124
+ let schemaMethod;
1125
+ switch (schemaType) {
1126
+ case "create":
1127
+ schemaMethod = ModelSchema.getCreateModelSchema;
1128
+ break;
1129
+ case "read":
1130
+ schemaMethod = ModelSchema.getReadModelSchema;
1131
+ break;
1132
+ case "update":
1133
+ schemaMethod = ModelSchema.getUpdateModelSchema;
1134
+ break;
1135
+ case "delete":
1136
+ schemaMethod = ModelSchema.getDeleteModelSchema;
1137
+ break;
1138
+ }
1139
+ zodType = z
1140
+ .array(z.lazy(() => {
1141
+ return schemaMethod({
1142
+ modelType: entityArrayType,
1143
+ });
1144
+ }))
1145
+ .openapi({
1146
+ type: "array",
1147
+ items: {
1148
+ type: "object",
1149
+ properties: {
1150
+ id: { type: "string" },
1151
+ },
1152
+ },
1153
+ example: [{ id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }],
1154
+ });
1155
+ }
1156
+ else if (column.type === TableColumnType.Entity) {
1157
+ const entityType = column.modelType;
1158
+ if (!entityType) {
1159
+ logger.debug(`Entity type is not defined for column ${key}`);
1160
+ return z.any().openapi({ type: "null", example: null });
1161
+ }
1162
+ // Use the appropriate schema method based on the operation type
1163
+ let schema;
1164
+ switch (schemaType) {
1165
+ case "create":
1166
+ schema = ModelSchema.getCreateModelSchema({ modelType: entityType });
1167
+ break;
1168
+ case "read":
1169
+ schema = ModelSchema.getReadModelSchema({ modelType: entityType });
1170
+ break;
1171
+ case "update":
1172
+ schema = ModelSchema.getUpdateModelSchema({ modelType: entityType });
1173
+ break;
1174
+ case "delete":
1175
+ schema = ModelSchema.getDeleteModelSchema({ modelType: entityType });
1176
+ break;
1177
+ }
1178
+ zodType = z
1179
+ .lazy(() => {
1180
+ return schema;
1181
+ })
1182
+ .openapi({
1183
+ type: "object",
1184
+ example: { id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" },
1185
+ });
1186
+ }
1187
+ else {
1188
+ zodType = z.any().openapi({ type: "null", example: null });
1189
+ }
1190
+ return zodType;
1191
+ }
1192
+ static getCreateModelSchema(data) {
1193
+ // Auto-generated fields to exclude from create schema
1194
+ const excludedFields = [
1195
+ "_id",
1196
+ "createdAt",
1197
+ "updatedAt",
1198
+ "deletedAt",
1199
+ "version",
1200
+ ];
1201
+ return this.buildModelSchema({
1202
+ modelType: data.modelType,
1203
+ excludedFields,
1204
+ schemaType: "create",
1205
+ description: "Create",
1206
+ example: this.getCreateSchemaExample(),
1207
+ });
1208
+ }
1209
+ static getReadModelSchema(data) {
1210
+ // For read operations, include all fields
1211
+ return this.buildModelSchema({
1212
+ modelType: data.modelType,
1213
+ schemaType: "read",
1214
+ description: "Read",
1215
+ example: this.getReadSchemaExample(),
1216
+ });
1217
+ }
1218
+ static getUpdateModelSchema(data) {
1219
+ // Auto-generated fields to exclude from update schema (but allow _id for identification)
1220
+ const excludedFields = [
1221
+ "createdAt",
1222
+ "updatedAt",
1223
+ "deletedAt",
1224
+ "version",
1225
+ ];
1226
+ return this.buildModelSchema({
1227
+ modelType: data.modelType,
1228
+ excludedFields,
1229
+ schemaType: "update",
1230
+ description: "Update",
1231
+ example: this.getUpdateSchemaExample(),
1232
+ makeOptional: true, // All fields are optional for updates
1233
+ });
1234
+ }
1235
+ static getDeleteModelSchema(data) {
1236
+ // For delete, we typically only need the ID
1237
+ const includedFields = ["_id"];
1238
+ return this.buildModelSchema({
1239
+ modelType: data.modelType,
1240
+ includedFields,
1241
+ schemaType: "delete",
1242
+ description: "Delete",
1243
+ example: this.getDeleteSchemaExample(),
1244
+ });
1245
+ }
1246
+ static getCreateSchemaExample() {
1247
+ return {
1248
+ name: "John Doe",
1249
+ email: "john@example.com",
1250
+ description: "Example user description",
1251
+ };
1252
+ }
1253
+ static getReadSchemaExample() {
1254
+ return {
1255
+ _id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
1256
+ name: "John Doe",
1257
+ email: "john@example.com",
1258
+ description: "Example user description",
1259
+ createdAt: "2023-01-15T12:30:00.000Z",
1260
+ updatedAt: "2023-01-15T12:30:00.000Z",
1261
+ version: 1,
1262
+ };
1263
+ }
1264
+ static getUpdateSchemaExample() {
1265
+ return {
1266
+ name: "Jane Doe",
1267
+ email: "jane@example.com",
1268
+ };
1269
+ }
1270
+ static getDeleteSchemaExample() {
1271
+ return {
1272
+ _id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
1273
+ };
361
1274
  }
362
1275
  }
363
1276
  //# sourceMappingURL=ModelSchema.js.map