@oneuptime/common 9.2.22 → 9.2.24

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 (51) hide show
  1. package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +10 -0
  2. package/Models/DatabaseModels/Alert.ts +2 -0
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +2 -0
  4. package/Models/DatabaseModels/Incident.ts +2 -0
  5. package/Models/DatabaseModels/IncidentState.ts +2 -0
  6. package/Models/DatabaseModels/Monitor.ts +2 -0
  7. package/Models/DatabaseModels/MonitorStatus.ts +2 -0
  8. package/Models/DatabaseModels/OnCallDutyPolicy.ts +2 -0
  9. package/Models/DatabaseModels/ScheduledMaintenance.ts +2 -0
  10. package/Models/DatabaseModels/StatusPage.ts +2 -0
  11. package/Models/DatabaseModels/Team.ts +2 -0
  12. package/Server/API/BaseAPI.ts +9 -3
  13. package/Server/API/StatusPageAPI.ts +22 -21
  14. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +25 -36
  15. package/Tests/Server/API/BaseAPI.test.ts +17 -11
  16. package/Types/Database/EnableMCP.ts +7 -0
  17. package/Types/ObjectID.ts +25 -0
  18. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +8 -0
  19. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
  20. package/build/dist/Models/DatabaseModels/Alert.js +2 -0
  21. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  22. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  23. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  24. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  25. package/build/dist/Models/DatabaseModels/IncidentState.js +2 -0
  26. package/build/dist/Models/DatabaseModels/IncidentState.js.map +1 -1
  27. package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
  28. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  29. package/build/dist/Models/DatabaseModels/MonitorStatus.js +2 -0
  30. package/build/dist/Models/DatabaseModels/MonitorStatus.js.map +1 -1
  31. package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js +2 -0
  32. package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js.map +1 -1
  33. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +2 -0
  34. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  35. package/build/dist/Models/DatabaseModels/StatusPage.js +2 -0
  36. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  37. package/build/dist/Models/DatabaseModels/Team.js +2 -0
  38. package/build/dist/Models/DatabaseModels/Team.js.map +1 -1
  39. package/build/dist/Server/API/BaseAPI.js +9 -3
  40. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  41. package/build/dist/Server/API/StatusPageAPI.js +15 -14
  42. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  43. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +22 -28
  44. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  45. package/build/dist/Tests/Server/API/BaseAPI.test.js +16 -11
  46. package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
  47. package/build/dist/Types/Database/EnableMCP.js +6 -0
  48. package/build/dist/Types/Database/EnableMCP.js.map +1 -0
  49. package/build/dist/Types/ObjectID.js +19 -0
  50. package/build/dist/Types/ObjectID.js.map +1 -1
  51. package/package.json +1 -1
@@ -44,6 +44,7 @@ export default class AnalyticsBaseModel extends CommonModel {
44
44
  partitionKey: string;
45
45
  projections?: Array<Projection> | undefined;
46
46
  materializedViews?: Array<MaterializedView> | undefined;
47
+ enableMCP?: boolean | undefined;
47
48
  }) {
48
49
  super({
49
50
  tableColumns: data.tableColumns,
@@ -146,6 +147,7 @@ export default class AnalyticsBaseModel extends CommonModel {
146
147
  this.partitionKey = data.partitionKey;
147
148
  this.projections = data.projections || [];
148
149
  this.materializedViews = data.materializedViews || [];
150
+ this.enableMCP = data.enableMCP || false;
149
151
  }
150
152
 
151
153
  private _enableWorkflowOn: EnableWorkflowOn | undefined;
@@ -272,6 +274,14 @@ export default class AnalyticsBaseModel extends CommonModel {
272
274
  this._materializedViews = v;
273
275
  }
274
276
 
277
+ private _enableMCP: boolean = false;
278
+ public get enableMCP(): boolean {
279
+ return this._enableMCP;
280
+ }
281
+ public set enableMCP(v: boolean) {
282
+ this._enableMCP = v;
283
+ }
284
+
275
285
  public getTenantColumn(): AnalyticsTableColumn | null {
276
286
  const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
277
287
  (column: AnalyticsTableColumn) => {
@@ -16,6 +16,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
16
16
  import ColumnType from "../../Types/Database/ColumnType";
17
17
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
18
18
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
19
+ import EnableMCP from "../../Types/Database/EnableMCP";
19
20
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
20
21
  import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
21
22
  import TableColumn from "../../Types/Database/TableColumn";
@@ -39,6 +40,7 @@ import { TelemetryQuery } from "../../Types/Telemetry/TelemetryQuery";
39
40
  import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
40
41
 
41
42
  @EnableDocumentation()
43
+ @EnableMCP()
42
44
  @AccessControlColumn("labels")
43
45
  @MultiTenentQueryAllowed(true)
44
46
  @TenantColumn("projectId")
@@ -124,6 +124,8 @@ export default class DatabaseBaseModel extends BaseEntity {
124
124
  public enableDocumentation!: boolean;
125
125
  public isMasterAdminApiDocs!: boolean;
126
126
 
127
+ public enableMCP!: boolean;
128
+
127
129
  public currentUserCanAccessColumnBy!: string | null;
128
130
  public slugifyColumn!: string | null;
129
131
  public saveSlugToColumn!: string | null;
@@ -17,6 +17,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
17
17
  import ColumnType from "../../Types/Database/ColumnType";
18
18
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
19
19
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
20
+ import EnableMCP from "../../Types/Database/EnableMCP";
20
21
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
21
22
  import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
22
23
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
@@ -42,6 +43,7 @@ import { TelemetryQuery } from "../../Types/Telemetry/TelemetryQuery";
42
43
  import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
43
44
 
44
45
  @EnableDocumentation()
46
+ @EnableMCP()
45
47
  @AccessControlColumn("labels")
46
48
  @MultiTenentQueryAllowed(true)
47
49
  @TenantColumn("projectId")
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
11
11
  import ColumnType from "../../Types/Database/ColumnType";
12
12
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
13
13
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
14
+ import EnableMCP from "../../Types/Database/EnableMCP";
14
15
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
15
16
  import ColorField from "../../Types/Database/ColorField";
16
17
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
@@ -31,6 +32,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
31
32
  delete: PlanType.Free,
32
33
  })
33
34
  @EnableDocumentation()
35
+ @EnableMCP()
34
36
  @TenantColumn("projectId")
35
37
  @TableAccessControl({
36
38
  create: [
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
11
11
  import ColumnType from "../../Types/Database/ColumnType";
12
12
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
13
13
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
14
+ import EnableMCP from "../../Types/Database/EnableMCP";
14
15
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
15
16
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
16
17
  import TableColumn from "../../Types/Database/TableColumn";
@@ -37,6 +38,7 @@ import {
37
38
  import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
38
39
 
39
40
  @EnableDocumentation()
41
+ @EnableMCP()
40
42
  @AccessControlColumn("labels")
41
43
  @TenantColumn("projectId")
42
44
  @TableAccessControl({
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
11
11
  import ColumnType from "../../Types/Database/ColumnType";
12
12
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
13
13
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
14
+ import EnableMCP from "../../Types/Database/EnableMCP";
14
15
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
15
16
  import ColorField from "../../Types/Database/ColorField";
16
17
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
@@ -31,6 +32,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
31
32
  delete: PlanType.Free,
32
33
  })
33
34
  @EnableDocumentation()
35
+ @EnableMCP()
34
36
  @TenantColumn("projectId")
35
37
  @TableAccessControl({
36
38
  create: [
@@ -10,6 +10,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
10
10
  import ColumnType from "../../Types/Database/ColumnType";
11
11
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
12
12
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
13
+ import EnableMCP from "../../Types/Database/EnableMCP";
13
14
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
14
15
  import TableColumn from "../../Types/Database/TableColumn";
15
16
  import TableColumnType from "../../Types/Database/TableColumnType";
@@ -32,6 +33,7 @@ import NotificationRuleWorkspaceChannel from "../../Types/Workspace/Notification
32
33
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
33
34
 
34
35
  @EnableDocumentation()
36
+ @EnableMCP()
35
37
  @AccessControlColumn("labels")
36
38
  @TenantColumn("projectId")
37
39
  @EnableWorkflow({
@@ -14,6 +14,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
14
14
  import ColumnType from "../../Types/Database/ColumnType";
15
15
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
16
16
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
17
+ import EnableMCP from "../../Types/Database/EnableMCP";
17
18
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
18
19
  import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
19
20
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
@@ -39,6 +40,7 @@ import Recurring from "../../Types/Events/Recurring";
39
40
  import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
40
41
 
41
42
  @EnableDocumentation()
43
+ @EnableMCP()
42
44
  @AccessControlColumn("labels")
43
45
  @MultiTenentQueryAllowed(true)
44
46
  @TenantColumn("projectId")
@@ -17,6 +17,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
17
17
  import ColumnType from "../../Types/Database/ColumnType";
18
18
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
19
19
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
20
+ import EnableMCP from "../../Types/Database/EnableMCP";
20
21
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
21
22
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
22
23
  import TableColumn from "../../Types/Database/TableColumn";
@@ -43,6 +44,7 @@ import {
43
44
  import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
44
45
 
45
46
  @EnableDocumentation()
47
+ @EnableMCP()
46
48
  @AccessControlColumn("labels")
47
49
  @TenantColumn("projectId")
48
50
  @TableAccessControl({
@@ -10,6 +10,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
10
10
  import ColumnType from "../../Types/Database/ColumnType";
11
11
  import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
12
12
  import EnableDocumentation from "../../Types/Database/EnableDocumentation";
13
+ import EnableMCP from "../../Types/Database/EnableMCP";
13
14
  import EnableWorkflow from "../../Types/Database/EnableWorkflow";
14
15
  import SlugifyColumn from "../../Types/Database/SlugifyColumn";
15
16
  import TableColumn from "../../Types/Database/TableColumn";
@@ -28,6 +29,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
28
29
  delete: PlanType.Free,
29
30
  })
30
31
  @EnableDocumentation()
32
+ @EnableMCP()
31
33
  @TenantColumn("projectId")
32
34
  @TableAccessControl({
33
35
  create: [
@@ -347,7 +347,9 @@ export default class BaseAPI<
347
347
  req: ExpressRequest,
348
348
  res: ExpressResponse,
349
349
  ): Promise<void> {
350
- const objectId: ObjectID = new ObjectID(req.params["id"] as string);
350
+ const idParam: string = req.params["id"] as string;
351
+ ObjectID.validateUUID(idParam);
352
+ const objectId: ObjectID = new ObjectID(idParam);
351
353
  await this.onBeforeGet(req, res);
352
354
  let select: Select<BaseModel> = {};
353
355
 
@@ -372,7 +374,9 @@ export default class BaseAPI<
372
374
  res: ExpressResponse,
373
375
  ): Promise<void> {
374
376
  await this.onBeforeDelete(req, res);
375
- const objectId: ObjectID = new ObjectID(req.params["id"] as string);
377
+ const idParam: string = req.params["id"] as string;
378
+ ObjectID.validateUUID(idParam);
379
+ const objectId: ObjectID = new ObjectID(idParam);
376
380
 
377
381
  await this.service.deleteOneById({
378
382
  id: objectId,
@@ -388,7 +392,9 @@ export default class BaseAPI<
388
392
  res: ExpressResponse,
389
393
  ): Promise<void> {
390
394
  await this.onBeforeUpdate(req, res);
391
- const objectId: ObjectID = new ObjectID(req.params["id"] as string);
395
+ const idParam: string = req.params["id"] as string;
396
+ ObjectID.validateUUID(idParam);
397
+ const objectId: ObjectID = new ObjectID(idParam);
392
398
  const objectIdString: string = objectId.toString();
393
399
  const body: JSONObject = req.body;
394
400
 
@@ -132,6 +132,7 @@ const resolveStatusPageIdOrThrow: ResolveStatusPageIdOrThrowFunction = async (
132
132
  }
133
133
 
134
134
  try {
135
+ ObjectID.validateUUID(statusPageIdOrDomain);
135
136
  return new ObjectID(statusPageIdOrDomain);
136
137
  } catch (err) {
137
138
  logger.error(
@@ -1399,12 +1400,12 @@ export default class StatusPageAPI extends BaseAPI<
1399
1400
  this.router.post(
1400
1401
  `${new this.entityType()
1401
1402
  .getCrudApiPath()
1402
- ?.toString()}/overview/:statusPageId`,
1403
+ ?.toString()}/overview/:statusPageIdOrDomain`,
1403
1404
  UserMiddleware.getUserMiddleware,
1404
1405
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1405
1406
  try {
1406
- const statusPageId: ObjectID = new ObjectID(
1407
- req.params["statusPageId"] as string,
1407
+ const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
1408
+ req.params["statusPageIdOrDomain"] as string,
1408
1409
  );
1409
1410
 
1410
1411
  await this.checkHasReadAccess({
@@ -1940,12 +1941,12 @@ export default class StatusPageAPI extends BaseAPI<
1940
1941
  this.router.post(
1941
1942
  `${new this.entityType()
1942
1943
  .getCrudApiPath()
1943
- ?.toString()}/incidents/:statusPageId`,
1944
+ ?.toString()}/incidents/:statusPageIdOrDomain`,
1944
1945
  UserMiddleware.getUserMiddleware,
1945
1946
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1946
1947
  try {
1947
- const objectId: ObjectID = new ObjectID(
1948
- req.params["statusPageId"] as string,
1948
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
1949
+ req.params["statusPageIdOrDomain"] as string,
1949
1950
  );
1950
1951
 
1951
1952
  const response: JSONObject = await this.getIncidents(
@@ -1964,12 +1965,12 @@ export default class StatusPageAPI extends BaseAPI<
1964
1965
  this.router.post(
1965
1966
  `${new this.entityType()
1966
1967
  .getCrudApiPath()
1967
- ?.toString()}/scheduled-maintenance-events/:statusPageId`,
1968
+ ?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`,
1968
1969
  UserMiddleware.getUserMiddleware,
1969
1970
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1970
1971
  try {
1971
- const objectId: ObjectID = new ObjectID(
1972
- req.params["statusPageId"] as string,
1972
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
1973
+ req.params["statusPageIdOrDomain"] as string,
1973
1974
  );
1974
1975
 
1975
1976
  const response: JSONObject = await this.getScheduledMaintenanceEvents(
@@ -1989,12 +1990,12 @@ export default class StatusPageAPI extends BaseAPI<
1989
1990
  this.router.post(
1990
1991
  `${new this.entityType()
1991
1992
  .getCrudApiPath()
1992
- ?.toString()}/announcements/:statusPageId`,
1993
+ ?.toString()}/announcements/:statusPageIdOrDomain`,
1993
1994
  UserMiddleware.getUserMiddleware,
1994
1995
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
1995
1996
  try {
1996
- const objectId: ObjectID = new ObjectID(
1997
- req.params["statusPageId"] as string,
1997
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
1998
+ req.params["statusPageIdOrDomain"] as string,
1998
1999
  );
1999
2000
 
2000
2001
  const response: JSONObject = await this.getAnnouncements(
@@ -2014,12 +2015,12 @@ export default class StatusPageAPI extends BaseAPI<
2014
2015
  this.router.post(
2015
2016
  `${new this.entityType()
2016
2017
  .getCrudApiPath()
2017
- ?.toString()}/incidents/:statusPageId/:incidentId`,
2018
+ ?.toString()}/incidents/:statusPageIdOrDomain/:incidentId`,
2018
2019
  UserMiddleware.getUserMiddleware,
2019
2020
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
2020
2021
  try {
2021
- const objectId: ObjectID = new ObjectID(
2022
- req.params["statusPageId"] as string,
2022
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
2023
+ req.params["statusPageIdOrDomain"] as string,
2023
2024
  );
2024
2025
 
2025
2026
  const incidentId: ObjectID = new ObjectID(
@@ -2042,12 +2043,12 @@ export default class StatusPageAPI extends BaseAPI<
2042
2043
  this.router.post(
2043
2044
  `${new this.entityType()
2044
2045
  .getCrudApiPath()
2045
- ?.toString()}/scheduled-maintenance-events/:statusPageId/:scheduledMaintenanceId`,
2046
+ ?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain/:scheduledMaintenanceId`,
2046
2047
  UserMiddleware.getUserMiddleware,
2047
2048
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
2048
2049
  try {
2049
- const objectId: ObjectID = new ObjectID(
2050
- req.params["statusPageId"] as string,
2050
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
2051
+ req.params["statusPageIdOrDomain"] as string,
2051
2052
  );
2052
2053
 
2053
2054
  const scheduledMaintenanceId: ObjectID = new ObjectID(
@@ -2071,12 +2072,12 @@ export default class StatusPageAPI extends BaseAPI<
2071
2072
  this.router.post(
2072
2073
  `${new this.entityType()
2073
2074
  .getCrudApiPath()
2074
- ?.toString()}/announcements/:statusPageId/:announcementId`,
2075
+ ?.toString()}/announcements/:statusPageIdOrDomain/:announcementId`,
2075
2076
  UserMiddleware.getUserMiddleware,
2076
2077
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
2077
2078
  try {
2078
- const objectId: ObjectID = new ObjectID(
2079
- req.params["statusPageId"] as string,
2079
+ const objectId: ObjectID = await resolveStatusPageIdOrThrow(
2080
+ req.params["statusPageIdOrDomain"] as string,
2080
2081
  );
2081
2082
 
2082
2083
  const announcementId: ObjectID = new ObjectID(
@@ -30,8 +30,6 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
30
30
  import BadDataException from "../../../../Types/Exception/BadDataException";
31
31
  import ObjectID from "../../../../Types/ObjectID";
32
32
  import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
33
- import WorkspaceUserAuthTokenService from "../../../Services/WorkspaceUserAuthTokenService";
34
- import WorkspaceUserAuthToken from "../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
35
33
  import WorkspaceProjectAuthToken, {
36
34
  MicrosoftTeamsMiscData,
37
35
  MicrosoftTeamsTeam,
@@ -2931,43 +2929,23 @@ All monitoring checks are passing normally.`;
2931
2929
  );
2932
2930
  }
2933
2931
 
2934
- // Try using user scoped token first when available
2932
+ // Use app-scoped token to fetch user's teams
2935
2933
  let allTeams: Array<JSONObject> = [];
2936
- let usedAccessToken: string | null = null;
2937
2934
 
2938
2935
  try {
2939
- // If caller provided a userAccessToken directly, use it
2940
- if (data.userAccessToken) {
2941
- logger.debug(
2942
- "Using provided user access token to fetch joined teams",
2943
- );
2944
- usedAccessToken = data.userAccessToken;
2936
+ // Fetch joined teams using app-scoped token
2937
+ if (data.userId) {
2938
+ logger.debug("Using app-scoped token to fetch joined teams for user");
2945
2939
  const userTeams: Record<string, { id: string; name: string }> =
2946
- await this.getUserJoinedTeams(usedAccessToken);
2947
- allTeams = Object.values(userTeams) as any;
2948
- } else if (data.userId) {
2949
- // Try to fetch stored user auth for this project + user
2950
- logger.debug("Looking up stored user auth token for provided userId");
2951
- const userAuth: WorkspaceUserAuthToken | null =
2952
- await WorkspaceUserAuthTokenService.getUserAuth({
2940
+ await this.getUserJoinedTeams({
2941
+ userId: data.userId.toString(),
2953
2942
  projectId: data.projectId,
2954
- userId: data.userId,
2955
- workspaceType: WorkspaceType.MicrosoftTeams,
2956
2943
  });
2957
-
2958
- if (userAuth && userAuth.authToken) {
2959
- usedAccessToken = userAuth.authToken;
2960
- logger.debug(
2961
- "Found user auth token; using it to fetch joined teams",
2962
- );
2963
- const userTeams: Record<string, { id: string; name: string }> =
2964
- await this.getUserJoinedTeams(usedAccessToken);
2965
- allTeams = Object.values(userTeams) as any;
2966
- }
2944
+ allTeams = Object.values(userTeams) as any;
2967
2945
  }
2968
2946
  } catch (err) {
2969
2947
  logger.warn(
2970
- "Failed to fetch teams using user-scoped token, falling back to app token:",
2948
+ "Failed to fetch teams using app-scoped token, falling back to paginated fetch:",
2971
2949
  );
2972
2950
  logger.warn(err);
2973
2951
  allTeams = [];
@@ -3082,19 +3060,30 @@ All monitoring checks are passing normally.`;
3082
3060
  }
3083
3061
  }
3084
3062
 
3085
- // Method to get user's joined teams using user access token
3063
+ // Method to get user's joined teams using app-scoped token
3086
3064
  @CaptureSpan()
3087
- public static async getUserJoinedTeams(
3088
- accessToken: string,
3089
- ): Promise<Record<string, { id: string; name: string }>> {
3065
+ public static async getUserJoinedTeams(data: {
3066
+ userId: string;
3067
+ projectId: ObjectID;
3068
+ }): Promise<Record<string, { id: string; name: string }>> {
3090
3069
  logger.debug("=== getUserJoinedTeams called ===");
3070
+ logger.debug(`User ID: ${data.userId}`);
3071
+ logger.debug(`Project ID: ${data.projectId.toString()}`);
3091
3072
 
3092
3073
  try {
3093
- // Get user's teams
3074
+ // Get a valid app access token (refreshed if needed)
3075
+ logger.debug("Refreshing app access token before fetching teams");
3076
+ const accessToken: string = await this.getValidAccessToken({
3077
+ authToken: "", // Not needed for app token refresh
3078
+ projectId: data.projectId,
3079
+ });
3080
+ logger.debug("App access token refreshed successfully");
3081
+
3082
+ // Get user's teams using app-scoped token
3094
3083
  const teamsResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
3095
3084
  await API.get<JSONObject>({
3096
3085
  url: URL.fromString(
3097
- "https://graph.microsoft.com/v1.0/me/joinedTeams",
3086
+ `https://graph.microsoft.com/v1.0/users/${data.userId}/joinedTeams`,
3098
3087
  ),
3099
3088
  headers: {
3100
3089
  Authorization: `Bearer ${accessToken}`,
@@ -116,18 +116,24 @@ const res = {
116
116
  send: jest.fn().mockReturnThis(),
117
117
  } as any as ExpressResponse;
118
118
 
119
+ // Valid UUIDs for test fixtures
120
+ const TEST_DELETE_ID: string = "550e8400-e29b-41d4-a716-446655440001";
121
+ const TEST_COUNT_ID: string = "550e8400-e29b-41d4-a716-446655440002";
122
+ const TEST_GET_ID: string = "550e8400-e29b-41d4-a716-446655440003";
123
+ const TEST_UPDATE_ID: string = "550e8400-e29b-41d4-a716-446655440004";
124
+
119
125
  const deleteRequest: OneUptimeRequest = {
120
- params: { id: "delete-me" },
126
+ params: { id: TEST_DELETE_ID },
121
127
  headers: {},
122
128
  } as unknown as OneUptimeRequest;
123
129
 
124
130
  const countRequest: OneUptimeRequest = {
125
- body: { query: { id: "count-me" } },
131
+ body: { query: { id: TEST_COUNT_ID } },
126
132
  headers: {},
127
133
  } as unknown as OneUptimeRequest;
128
134
 
129
135
  const getRequest: OneUptimeRequest = {
130
- params: { id: "get-me" },
136
+ params: { id: TEST_GET_ID },
131
137
  headers: {},
132
138
  } as unknown as OneUptimeRequest;
133
139
 
@@ -524,7 +530,7 @@ describe("BaseAPI", () => {
524
530
  const countBySpy: jest.SpyInstance = jest.spyOn(TestService, "countBy");
525
531
  await baseApiInstance.count(countRequest, res);
526
532
  expect(countBySpy).toHaveBeenCalledWith({
527
- query: { id: "count-me" },
533
+ query: { id: TEST_COUNT_ID },
528
534
  props: emptyDatabaseCommonInteractionProps,
529
535
  });
530
536
  });
@@ -533,7 +539,7 @@ describe("BaseAPI", () => {
533
539
  const findBySpy: jest.SpyInstance = jest.spyOn(TestService, "countBy");
534
540
  await baseApiInstance.count(countRequest, res);
535
541
  expect(findBySpy).toHaveBeenCalledWith({
536
- query: { id: "count-me" },
542
+ query: { id: TEST_COUNT_ID },
537
543
  props: emptyDatabaseCommonInteractionProps,
538
544
  });
539
545
  });
@@ -568,7 +574,7 @@ describe("BaseAPI", () => {
568
574
  await baseApiInstance.getItem(getRequest, res);
569
575
  expect(findOneByIdSpy).toHaveBeenCalledWith(
570
576
  expect.objectContaining({
571
- id: new ObjectID("get-me"),
577
+ id: new ObjectID(TEST_GET_ID),
572
578
  props: emptyDatabaseCommonInteractionProps,
573
579
  select: {},
574
580
  }),
@@ -588,7 +594,7 @@ describe("BaseAPI", () => {
588
594
  await baseApiInstance.getItem(getRequestWithSelect, res);
589
595
  expect(findOneByIdSpy).toHaveBeenCalledWith(
590
596
  expect.objectContaining({
591
- id: new ObjectID("get-me"),
597
+ id: new ObjectID(TEST_GET_ID),
592
598
  props: emptyDatabaseCommonInteractionProps,
593
599
  select: { id: true },
594
600
  }),
@@ -630,7 +636,7 @@ describe("BaseAPI", () => {
630
636
 
631
637
  expect(deleteOneBySpy).toHaveBeenCalledWith(
632
638
  expect.objectContaining({
633
- id: new ObjectID("delete-me"),
639
+ id: new ObjectID(TEST_DELETE_ID),
634
640
  props: emptyDatabaseCommonInteractionProps,
635
641
  }),
636
642
  );
@@ -653,7 +659,7 @@ describe("BaseAPI", () => {
653
659
 
654
660
  beforeEach(() => {
655
661
  updateRequest = {
656
- params: { id: "update-me" },
662
+ params: { id: TEST_UPDATE_ID },
657
663
  body: { data: { name: "updatedName" } },
658
664
  headers: {},
659
665
  } as unknown as OneUptimeRequest;
@@ -714,7 +720,7 @@ describe("BaseAPI", () => {
714
720
 
715
721
  expect(updateOneBySpy).toHaveBeenCalledWith(
716
722
  expect.objectContaining({
717
- id: new ObjectID("update-me"),
723
+ id: new ObjectID(TEST_UPDATE_ID),
718
724
  }),
719
725
  );
720
726
  });
@@ -726,7 +732,7 @@ describe("BaseAPI", () => {
726
732
  "updateOneById",
727
733
  );
728
734
  expect(updateOneBySpy).toHaveBeenCalledWith({
729
- id: new ObjectID("update-me"),
735
+ id: new ObjectID(TEST_UPDATE_ID),
730
736
  data: { name: "updatedName" },
731
737
  props: emptyProps,
732
738
  });
@@ -0,0 +1,7 @@
1
+ import GenericFunction from "../GenericFunction";
2
+
3
+ export default () => {
4
+ return (ctr: GenericFunction) => {
5
+ ctr.prototype.enableMCP = true;
6
+ };
7
+ };
package/Types/ObjectID.ts CHANGED
@@ -6,6 +6,10 @@ import { JSONObject, ObjectType } from "./JSON";
6
6
  import { FindOperator } from "typeorm";
7
7
  import Zod from "../Utils/Schema/Zod";
8
8
 
9
+ // UUID validation regex - matches standard UUID format
10
+ const UUID_REGEX: RegExp =
11
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12
+
9
13
  export default class ObjectID extends DatabaseProperty {
10
14
  private _id: string = "";
11
15
  public get id(): string {
@@ -112,6 +116,27 @@ export default class ObjectID extends DatabaseProperty {
112
116
  return new ObjectID(id);
113
117
  }
114
118
 
119
+ /**
120
+ * Check if a string is a valid UUID format
121
+ */
122
+ public static isValidUUID(id: string): boolean {
123
+ if (!id || typeof id !== "string") {
124
+ return false;
125
+ }
126
+ return UUID_REGEX.test(id);
127
+ }
128
+
129
+ /**
130
+ * Validate that a string is a valid UUID, throw BadDataException if not
131
+ */
132
+ public static validateUUID(id: string): void {
133
+ if (!ObjectID.isValidUUID(id.toString())) {
134
+ throw new BadDataException(
135
+ `Invalid ID format: "${id}". Expected a valid UUID (e.g., "550e8400-e29b-41d4-a716-446655440000").`,
136
+ );
137
+ }
138
+ }
139
+
115
140
  public static override getSchema(): any {
116
141
  return Zod.string().openapi({
117
142
  type: "string",
@@ -20,6 +20,7 @@ export default class AnalyticsBaseModel extends CommonModel {
20
20
  this._tableName = "";
21
21
  this._projections = [];
22
22
  this._materializedViews = [];
23
+ this._enableMCP = false;
23
24
  this.tableName = data.tableName;
24
25
  const columns = [...data.tableColumns];
25
26
  if (data.tableEngine) {
@@ -87,6 +88,7 @@ export default class AnalyticsBaseModel extends CommonModel {
87
88
  this.partitionKey = data.partitionKey;
88
89
  this.projections = data.projections || [];
89
90
  this.materializedViews = data.materializedViews || [];
91
+ this.enableMCP = data.enableMCP || false;
90
92
  }
91
93
  get enableWorkflowOn() {
92
94
  return this._enableWorkflowOn;
@@ -178,6 +180,12 @@ export default class AnalyticsBaseModel extends CommonModel {
178
180
  set materializedViews(v) {
179
181
  this._materializedViews = v;
180
182
  }
183
+ get enableMCP() {
184
+ return this._enableMCP;
185
+ }
186
+ set enableMCP(v) {
187
+ this._enableMCP = v;
188
+ }
181
189
  getTenantColumn() {
182
190
  const column = this.tableColumns.find((column) => {
183
191
  return column.isTenantId;