@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
@@ -1,18 +1,18 @@
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
6
  import BadDataException from "../../Types/Exception/BadDataException";
8
7
  import { PermissionHelper } from "../../Types/Permission";
9
- export class ModelSchema {
8
+ import { BaseSchema } from "./BaseSchema";
9
+ export class ModelSchema extends BaseSchema {
10
10
  /**
11
11
  * Format permissions array into a human-readable string for OpenAPI documentation
12
12
  */
13
13
  static formatPermissionsForSchema(permissions) {
14
14
  if (!permissions || permissions.length === 0) {
15
- return "No permissions required";
15
+ return "No access - you don't have permission for this operation";
16
16
  }
17
17
  return PermissionHelper.getPermissionTitles(permissions).join(", ");
18
18
  }
@@ -34,11 +34,23 @@ export class ModelSchema {
34
34
  const model = new modelType();
35
35
  const columns = getTableColumns(model);
36
36
  const shape = {};
37
+ // Get column access control for permission filtering
38
+ const columnAccessControl = model.getColumnAccessControlForAllColumns();
37
39
  for (const key in columns) {
38
40
  const column = columns[key];
39
41
  if (!column) {
40
42
  continue;
41
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
+ }
42
54
  let zodType;
43
55
  if (column.type === TableColumnType.ObjectID) {
44
56
  zodType = z.string().openapi({
@@ -331,52 +343,33 @@ export class ModelSchema {
331
343
  static getQueryModelSchema(data) {
332
344
  const modelType = data.modelType;
333
345
  const model = new modelType();
334
- const columns = getTableColumns(model);
335
- const shape = {};
336
- for (const key in columns) {
337
- const column = columns[key];
338
- if (!column) {
339
- continue;
340
- }
341
- // Get valid operators for this column type
342
- const validOperators = this.getValidOperatorsForColumnType(column.type);
343
- if (validOperators.length === 0) {
344
- continue;
345
- }
346
- // Create a union type of all valid operators for this column
347
- const operatorSchemas = validOperators.map((operatorType) => {
348
- return this.getOperatorSchema(operatorType, column.type);
349
- });
350
- let columnSchema;
351
- if (operatorSchemas.length === 1 && operatorSchemas[0]) {
352
- columnSchema = operatorSchemas[0].optional();
353
- }
354
- else if (operatorSchemas.length > 1) {
355
- columnSchema = z
356
- .union(operatorSchemas)
357
- .optional();
358
- }
359
- else {
360
- // Fallback for empty operators array
361
- columnSchema = z.any().optional();
362
- }
363
- // Add OpenAPI documentation for query operators
364
- const operatorExamples = this.getQueryOperatorExamples(column.type, validOperators);
365
- columnSchema = columnSchema.openapi({
366
- type: "object",
367
- description: `Query operators for ${key} field of type ${column.type}. Supported operators: ${validOperators.join(", ")}`,
368
- example: operatorExamples.length > 0 && operatorExamples[0]
369
- ? operatorExamples[0].example
370
- : {},
371
- });
372
- shape[key] = columnSchema;
373
- }
374
- const schema = z.object(shape).openapi({
375
- type: "object",
376
- description: `Query schema for ${model.tableName || "model"} model. Each field can use various operators based on its data type.`,
377
- example: this.getQuerySchemaExample(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
+ },
378
372
  });
379
- return schema;
380
373
  }
381
374
  static getValidOperatorsForColumnType(columnType) {
382
375
  const commonOperators = [
@@ -546,303 +539,102 @@ export class ModelSchema {
546
539
  static getSortModelSchema(data) {
547
540
  const modelType = data.modelType;
548
541
  const model = new modelType();
549
- const columns = getTableColumns(model);
550
- const shape = {};
551
- for (const key in columns) {
552
- const column = columns[key];
553
- if (!column) {
554
- continue;
555
- }
556
- const isSortable = ModelSchema.getSortableTypes().includes(column.type);
557
- if (!isSortable) {
558
- continue;
559
- }
560
- shape[key] = z
561
- .enum([SortOrder.Ascending, SortOrder.Descending])
562
- .optional()
563
- .openapi({
564
- type: "string",
565
- enum: [SortOrder.Ascending, SortOrder.Descending],
566
- description: `Sort order for ${key} field`,
567
- example: SortOrder.Ascending,
568
- });
569
- }
570
- return z.object(shape).openapi({
571
- type: "object",
572
- description: `Sort schema for ${model.tableName || "model"} model. Only sortable fields are included.`,
573
- example: this.getSortSchemaExample(),
574
- additionalProperties: false,
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
+ },
575
559
  });
576
560
  }
577
561
  static getSelectModelSchema(data) {
578
562
  const modelType = data.modelType;
579
563
  const model = new modelType();
580
- const columns = getTableColumns(model);
581
- const shape = {};
582
- for (const key in columns) {
583
- const column = columns[key];
584
- if (!column) {
585
- continue;
586
- }
587
- // if its entity array or entity then you can select nested properties
588
- if (!data.isNested &&
589
- column.modelType &&
590
- (column.type === TableColumnType.EntityArray ||
591
- column.type === TableColumnType.Entity)) {
592
- // can only do one level of nesting
593
- shape[key] = this.getSelectModelSchema({
594
- modelType: column.modelType,
595
- isNested: true,
596
- }).openapi({
597
- type: "object",
598
- description: `Select fields for nested ${key} entity`,
599
- example: { id: true, name: true },
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;
600
576
  });
601
- continue;
602
- }
603
- shape[key] = z
604
- .boolean()
605
- .optional()
606
- .openapi({
607
- type: "boolean",
608
- description: `Select ${key} field in the response`,
609
- example: true,
610
- });
611
- }
612
- return z.object(shape).openapi({
613
- type: "object",
614
- description: `Select schema for ${model.tableName || "model"} model. Set fields to true to include them in the response.`,
615
- example: this.getSelectSchemaExample(modelType),
616
- additionalProperties: false,
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
+ },
617
596
  });
618
597
  }
619
598
  static getGroupByModelSchema(data) {
620
599
  const modelType = data.modelType;
621
600
  const model = new modelType();
622
- const columns = getTableColumns(model);
623
- const shape = {};
624
- for (const key in columns) {
625
- const column = columns[key];
626
- if (!column) {
627
- continue;
628
- }
629
- // Only allow grouping by certain field types that make sense for aggregation
630
- const isGroupable = [
631
- TableColumnType.ShortText,
632
- TableColumnType.LongText,
633
- TableColumnType.Name,
634
- TableColumnType.Email,
635
- TableColumnType.Slug,
636
- TableColumnType.ObjectID,
637
- TableColumnType.Boolean,
638
- TableColumnType.Date,
639
- TableColumnType.Number,
640
- TableColumnType.PositiveNumber,
641
- TableColumnType.SmallNumber,
642
- TableColumnType.SmallPositiveNumber,
643
- TableColumnType.BigNumber,
644
- TableColumnType.BigPositiveNumber,
645
- ].includes(column.type);
646
- if (!isGroupable) {
647
- continue;
648
- }
649
- shape[key] = z
650
- .literal(true)
651
- .optional()
652
- .openapi({
653
- type: "boolean",
654
- description: `Group by ${key} field. Only one field can be selected for grouping.`,
655
- example: true,
656
- });
657
- }
658
- return z.object(shape).openapi({
659
- type: "object",
660
- description: `Group by schema for ${model.tableName || "model"} model. Only one field can be set to true for grouping.`,
661
- example: this.getGroupBySchemaExample(modelType),
662
- additionalProperties: false,
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
+ },
663
636
  });
664
637
  }
665
- static getQueryOperatorExamples(columnType, validOperators) {
666
- const examples = [];
667
- for (const operator of validOperators) {
668
- switch (operator) {
669
- case "EqualTo":
670
- examples.push({
671
- properties: {
672
- _type: { type: "string", enum: ["EqualTo"] },
673
- value: { type: this.getOpenAPITypeForColumn(columnType) },
674
- },
675
- required: ["_type", "value"],
676
- example: {
677
- _type: "EqualTo",
678
- value: this.getExampleValueForColumn(columnType),
679
- },
680
- });
681
- break;
682
- case "NotEqual":
683
- examples.push({
684
- properties: {
685
- _type: { type: "string", enum: ["NotEqual"] },
686
- value: { type: this.getOpenAPITypeForColumn(columnType) },
687
- },
688
- required: ["_type", "value"],
689
- example: {
690
- _type: "NotEqual",
691
- value: this.getExampleValueForColumn(columnType),
692
- },
693
- });
694
- break;
695
- case "Search":
696
- examples.push({
697
- properties: {
698
- _type: { type: "string", enum: ["Search"] },
699
- value: { type: "string" },
700
- },
701
- required: ["_type", "value"],
702
- example: { _type: "Search", value: "search term" },
703
- });
704
- break;
705
- case "GreaterThan":
706
- examples.push({
707
- properties: {
708
- _type: { type: "string", enum: ["GreaterThan"] },
709
- value: { type: this.getOpenAPITypeForColumn(columnType) },
710
- },
711
- required: ["_type", "value"],
712
- example: {
713
- _type: "GreaterThan",
714
- value: this.getExampleValueForColumn(columnType),
715
- },
716
- });
717
- break;
718
- case "LessThan":
719
- examples.push({
720
- properties: {
721
- _type: { type: "string", enum: ["LessThan"] },
722
- value: { type: this.getOpenAPITypeForColumn(columnType) },
723
- },
724
- required: ["_type", "value"],
725
- example: {
726
- _type: "LessThan",
727
- value: this.getExampleValueForColumn(columnType),
728
- },
729
- });
730
- break;
731
- case "GreaterThanOrEqual":
732
- examples.push({
733
- properties: {
734
- _type: { type: "string", enum: ["GreaterThanOrEqual"] },
735
- value: { type: this.getOpenAPITypeForColumn(columnType) },
736
- },
737
- required: ["_type", "value"],
738
- example: {
739
- _type: "GreaterThanOrEqual",
740
- value: this.getExampleValueForColumn(columnType),
741
- },
742
- });
743
- break;
744
- case "LessThanOrEqual":
745
- examples.push({
746
- properties: {
747
- _type: { type: "string", enum: ["LessThanOrEqual"] },
748
- value: { type: this.getOpenAPITypeForColumn(columnType) },
749
- },
750
- required: ["_type", "value"],
751
- example: {
752
- _type: "LessThanOrEqual",
753
- value: this.getExampleValueForColumn(columnType),
754
- },
755
- });
756
- break;
757
- case "EqualToOrNull":
758
- examples.push({
759
- properties: {
760
- _type: { type: "string", enum: ["EqualToOrNull"] },
761
- value: { type: this.getOpenAPITypeForColumn(columnType) },
762
- },
763
- required: ["_type", "value"],
764
- example: {
765
- _type: "EqualToOrNull",
766
- value: this.getExampleValueForColumn(columnType),
767
- },
768
- });
769
- break;
770
- case "InBetween":
771
- examples.push({
772
- properties: {
773
- _type: { type: "string", enum: ["InBetween"] },
774
- startValue: { type: this.getOpenAPITypeForColumn(columnType) },
775
- endValue: { type: this.getOpenAPITypeForColumn(columnType) },
776
- },
777
- required: ["_type", "startValue", "endValue"],
778
- example: {
779
- _type: "InBetween",
780
- startValue: this.getExampleValueForColumn(columnType),
781
- endValue: this.getExampleValueForColumn(columnType, true),
782
- },
783
- });
784
- break;
785
- case "IsNull":
786
- examples.push({
787
- properties: {
788
- _type: { type: "string", enum: ["IsNull"] },
789
- },
790
- required: ["_type"],
791
- example: { _type: "IsNull" },
792
- });
793
- break;
794
- case "NotNull":
795
- examples.push({
796
- properties: {
797
- _type: { type: "string", enum: ["NotNull"] },
798
- },
799
- required: ["_type"],
800
- example: { _type: "NotNull" },
801
- });
802
- break;
803
- case "Includes":
804
- examples.push({
805
- properties: {
806
- _type: { type: "string", enum: ["Includes"] },
807
- value: {
808
- type: "array",
809
- items: { type: this.getOpenAPITypeForColumn(columnType) },
810
- },
811
- },
812
- required: ["_type", "value"],
813
- example: {
814
- _type: "Includes",
815
- value: [
816
- this.getExampleValueForColumn(columnType),
817
- this.getExampleValueForColumn(columnType, true),
818
- ],
819
- },
820
- });
821
- break;
822
- }
823
- }
824
- return examples;
825
- }
826
- static getOpenAPITypeForColumn(columnType) {
827
- switch (columnType) {
828
- case TableColumnType.Number:
829
- case TableColumnType.PositiveNumber:
830
- case TableColumnType.SmallNumber:
831
- case TableColumnType.SmallPositiveNumber:
832
- case TableColumnType.BigNumber:
833
- case TableColumnType.BigPositiveNumber:
834
- return "number";
835
- case TableColumnType.Boolean:
836
- return "boolean";
837
- case TableColumnType.Date:
838
- return "string";
839
- case TableColumnType.JSON:
840
- case TableColumnType.Array:
841
- return "object";
842
- default:
843
- return "string";
844
- }
845
- }
846
638
  static getExampleValueForColumn(columnType, isSecondValue = false) {
847
639
  switch (columnType) {
848
640
  case TableColumnType.ObjectID:
@@ -954,11 +746,6 @@ export class ModelSchema {
954
746
  }
955
747
  return example;
956
748
  }
957
- static getSortSchemaExample() {
958
- return {
959
- createdAt: "Descending",
960
- };
961
- }
962
749
  static getSelectSchemaExample(modelType) {
963
750
  if (!modelType) {
964
751
  throw new BadDataException("Model type is required to generate select schema example.");
@@ -1050,6 +837,8 @@ export class ModelSchema {
1050
837
  const model = new modelType();
1051
838
  const columns = getTableColumns(model);
1052
839
  const shape = {};
840
+ // Get column access control for permission filtering
841
+ const columnAccessControl = model.getColumnAccessControlForAllColumns();
1053
842
  for (const key in columns) {
1054
843
  const column = columns[key];
1055
844
  if (!column) {
@@ -1063,6 +852,35 @@ export class ModelSchema {
1063
852
  if (data.includedFields && !data.includedFields.includes(key)) {
1064
853
  continue;
1065
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
+ }
1066
884
  let zodType = this.getZodTypeForColumn(column, key, data.schemaType);
1067
885
  // Make fields optional if specified
1068
886
  if (data.makeOptional) {