From 885a6ca36ea991cd2d43bd6950338d31a3271a8b Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Mon, 1 Jul 2024 14:02:49 +0100 Subject: [PATCH] feat: Add ProbeOwnerTeam and ProbeOwnerUser APIs This commit adds the APIs for ProbeOwnerTeam and ProbeOwnerUser, allowing users to manage ownership of probes within the application. The APIs are implemented in the BaseAPI/Index.ts file and include routes for creating, deleting, editing, and reading probe owner teams and users. This enhancement improves the functionality of the application by providing a way to manage probe ownership. --- App/FeatureSet/BaseAPI/Index.ts | 28 +++++++++ .../Jobs/Probe/UpdateConnectionStatus.ts | 11 +--- .../1719838746775-MigrationName.ts | 62 +++++++++++++++++++ .../Postgres/SchemaMigrations/Index.ts | 2 + Model/Models/Probe.ts | 55 +++++++++++++++- 5 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 CommonServer/Infrastructure/Postgres/SchemaMigrations/1719838746775-MigrationName.ts diff --git a/App/FeatureSet/BaseAPI/Index.ts b/App/FeatureSet/BaseAPI/Index.ts index 12876e9daa..d8ac3410c8 100644 --- a/App/FeatureSet/BaseAPI/Index.ts +++ b/App/FeatureSet/BaseAPI/Index.ts @@ -286,6 +286,16 @@ import WorkflowService, { import WorkflowVariableService, { Service as WorkflowVariableServiceType, } from "CommonServer/Services/WorkflowVariableService"; + +import ProbeOwnerTeamService, { + Service as ProbeOwnerTeamServiceType, +} from "CommonServer/Services/ProbeOwnerTeamService"; + +import ProbeOwnerUserService, { + Service as ProbeOwnerUserServiceType, +} from "CommonServer/Services/ProbeOwnerUserService"; + + import FeatureSet from "CommonServer/Types/FeatureSet"; import Express, { ExpressApplication } from "CommonServer/Utils/Express"; import Log from "Model/AnalyticsModels/Log"; @@ -377,6 +387,8 @@ import UserOnCallLog from "Model/Models/UserOnCallLog"; import Workflow from "Model/Models/Workflow"; import WorkflowLog from "Model/Models/WorkflowLog"; import WorkflowVariable from "Model/Models/WorkflowVariable"; +import ProbeOwnerTeam from "Model/Models/ProbeOwnerTeam"; +import ProbeOwnerUser from "Model/Models/ProbeOwnerUser"; const BaseAPIFeatureSet: FeatureSet = { init: async (): Promise => { @@ -448,6 +460,22 @@ const BaseAPIFeatureSet: FeatureSet = { ).getRouter(), ); + app.use( + `/${APP_NAME.toLocaleLowerCase()}`, + new BaseAPI( + ProbeOwnerUser, + ProbeOwnerUserService, + ).getRouter(), + ); + + app.use( + `/${APP_NAME.toLocaleLowerCase()}`, + new BaseAPI( + ProbeOwnerTeam, + ProbeOwnerTeamService, + ).getRouter(), + ); + app.use( `/${APP_NAME.toLocaleLowerCase()}`, new BaseAPI( diff --git a/App/FeatureSet/Workers/Jobs/Probe/UpdateConnectionStatus.ts b/App/FeatureSet/Workers/Jobs/Probe/UpdateConnectionStatus.ts index b32553a5e1..2b5775d0b0 100644 --- a/App/FeatureSet/Workers/Jobs/Probe/UpdateConnectionStatus.ts +++ b/App/FeatureSet/Workers/Jobs/Probe/UpdateConnectionStatus.ts @@ -5,7 +5,6 @@ import { EVERY_MINUTE } from "Common/Utils/CronTime"; import ProbeService from "CommonServer/Services/ProbeService"; import logger from "CommonServer/Utils/Logger"; import Probe, { ProbeStatus } from "Model/Models/Probe"; -import User from "Model/Models/User"; import MonitorProbe from "Model/Models/MonitorProbe"; import MonitorProbeService from "CommonServer/Services/MonitorProbeService"; @@ -41,7 +40,7 @@ RunCron( continue; } - let connectionStatus = ProbeStatus.Connected; + let connectionStatus: ProbeStatus = ProbeStatus.Connected; if (!probe.lastAlive) { connectionStatus = ProbeStatus.Disconnected; @@ -59,8 +58,8 @@ RunCron( connectionStatus = ProbeStatus.Connected; } - let shouldNotifyProbeOwner = false; - let shouldUpdateConnectionStatus = false; + let shouldNotifyProbeOwner: boolean = false; + let shouldUpdateConnectionStatus: boolean = false; if ( probe.connectionStatus && @@ -114,7 +113,3 @@ RunCron( } }, ); - -const getProbeOwners = async (probe: Probe): Promise => {}; - -const updateMonitors = async (probe: Probe): Promise => {}; diff --git a/CommonServer/Infrastructure/Postgres/SchemaMigrations/1719838746775-MigrationName.ts b/CommonServer/Infrastructure/Postgres/SchemaMigrations/1719838746775-MigrationName.ts new file mode 100644 index 0000000000..9ccd5bd78c --- /dev/null +++ b/CommonServer/Infrastructure/Postgres/SchemaMigrations/1719838746775-MigrationName.ts @@ -0,0 +1,62 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MigrationName1719838746775 implements MigrationInterface { + public name = 'MigrationName1719838746775' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "ProbeOwnerTeam" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "version" integer NOT NULL, "projectId" uuid NOT NULL, "teamId" uuid NOT NULL, "probeId" uuid NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "isOwnerNotified" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_b33e4c1d912b244680e0bace977" PRIMARY KEY ("_id"))`); + await queryRunner.query(`CREATE INDEX "IDX_6ffffa98ea1d05063733f36e55" ON "ProbeOwnerTeam" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_90007d943ad04dbef87aefcb6b" ON "ProbeOwnerTeam" ("teamId") `); + await queryRunner.query(`CREATE INDEX "IDX_d9a4b1d48b2eaefefb8f5dceb7" ON "ProbeOwnerTeam" ("probeId") `); + await queryRunner.query(`CREATE INDEX "IDX_b0a43c04a374168e90b5fe63e7" ON "ProbeOwnerTeam" ("isOwnerNotified") `); + await queryRunner.query(`CREATE TABLE "ProbeOwnerUser" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "version" integer NOT NULL, "projectId" uuid NOT NULL, "userId" uuid NOT NULL, "probeId" uuid NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "isOwnerNotified" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6dfa74aadf4e90010fc1e86c76b" PRIMARY KEY ("_id"))`); + await queryRunner.query(`CREATE INDEX "IDX_e94247bb495236a2931d974cf0" ON "ProbeOwnerUser" ("projectId") `); + await queryRunner.query(`CREATE INDEX "IDX_3fbca1f838f73adbba73e81de7" ON "ProbeOwnerUser" ("userId") `); + await queryRunner.query(`CREATE INDEX "IDX_5d293a0a28ae8b189814761e9b" ON "ProbeOwnerUser" ("probeId") `); + await queryRunner.query(`CREATE INDEX "IDX_da7e9e3e1f350f3f99f0271d85" ON "ProbeOwnerUser" ("isOwnerNotified") `); + await queryRunner.query(`CREATE TABLE "ProbeLabel" ("probeId" uuid NOT NULL, "labelId" uuid NOT NULL, CONSTRAINT "PK_4564a1667bf06e98f36c266e986" PRIMARY KEY ("probeId", "labelId"))`); + await queryRunner.query(`CREATE INDEX "IDX_171fad9149ab82a7e83d12286b" ON "ProbeLabel" ("probeId") `); + await queryRunner.query(`CREATE INDEX "IDX_ab7fd85397d8c70c77c761004d" ON "ProbeLabel" ("labelId") `); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" ADD CONSTRAINT "FK_6ffffa98ea1d05063733f36e557" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" ADD CONSTRAINT "FK_90007d943ad04dbef87aefcb6b7" FOREIGN KEY ("teamId") REFERENCES "Team"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" ADD CONSTRAINT "FK_d9a4b1d48b2eaefefb8f5dceb73" FOREIGN KEY ("probeId") REFERENCES "Probe"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" ADD CONSTRAINT "FK_5f7e1a1dfc8380824e10d83f124" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" ADD CONSTRAINT "FK_e72250699a438e22153d9c32ea3" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" ADD CONSTRAINT "FK_e94247bb495236a2931d974cf0a" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" ADD CONSTRAINT "FK_3fbca1f838f73adbba73e81de7a" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" ADD CONSTRAINT "FK_5d293a0a28ae8b189814761e9b7" FOREIGN KEY ("probeId") REFERENCES "Probe"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" ADD CONSTRAINT "FK_011c2a132409253ceb1234695c6" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" ADD CONSTRAINT "FK_248df6f39557f114b03dd815bcf" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "ProbeLabel" ADD CONSTRAINT "FK_171fad9149ab82a7e83d12286b3" FOREIGN KEY ("probeId") REFERENCES "Probe"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); + await queryRunner.query(`ALTER TABLE "ProbeLabel" ADD CONSTRAINT "FK_ab7fd85397d8c70c77c761004d2" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "ProbeLabel" DROP CONSTRAINT "FK_ab7fd85397d8c70c77c761004d2"`); + await queryRunner.query(`ALTER TABLE "ProbeLabel" DROP CONSTRAINT "FK_171fad9149ab82a7e83d12286b3"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" DROP CONSTRAINT "FK_248df6f39557f114b03dd815bcf"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" DROP CONSTRAINT "FK_011c2a132409253ceb1234695c6"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" DROP CONSTRAINT "FK_5d293a0a28ae8b189814761e9b7"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" DROP CONSTRAINT "FK_3fbca1f838f73adbba73e81de7a"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerUser" DROP CONSTRAINT "FK_e94247bb495236a2931d974cf0a"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" DROP CONSTRAINT "FK_e72250699a438e22153d9c32ea3"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" DROP CONSTRAINT "FK_5f7e1a1dfc8380824e10d83f124"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" DROP CONSTRAINT "FK_d9a4b1d48b2eaefefb8f5dceb73"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" DROP CONSTRAINT "FK_90007d943ad04dbef87aefcb6b7"`); + await queryRunner.query(`ALTER TABLE "ProbeOwnerTeam" DROP CONSTRAINT "FK_6ffffa98ea1d05063733f36e557"`); + await queryRunner.query(`DROP INDEX "public"."IDX_ab7fd85397d8c70c77c761004d"`); + await queryRunner.query(`DROP INDEX "public"."IDX_171fad9149ab82a7e83d12286b"`); + await queryRunner.query(`DROP TABLE "ProbeLabel"`); + await queryRunner.query(`DROP INDEX "public"."IDX_da7e9e3e1f350f3f99f0271d85"`); + await queryRunner.query(`DROP INDEX "public"."IDX_5d293a0a28ae8b189814761e9b"`); + await queryRunner.query(`DROP INDEX "public"."IDX_3fbca1f838f73adbba73e81de7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_e94247bb495236a2931d974cf0"`); + await queryRunner.query(`DROP TABLE "ProbeOwnerUser"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b0a43c04a374168e90b5fe63e7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_d9a4b1d48b2eaefefb8f5dceb7"`); + await queryRunner.query(`DROP INDEX "public"."IDX_90007d943ad04dbef87aefcb6b"`); + await queryRunner.query(`DROP INDEX "public"."IDX_6ffffa98ea1d05063733f36e55"`); + await queryRunner.query(`DROP TABLE "ProbeOwnerTeam"`); + } + +} diff --git a/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts b/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts index b5ccbfa65d..8b865d5fb5 100644 --- a/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts +++ b/CommonServer/Infrastructure/Postgres/SchemaMigrations/Index.ts @@ -21,6 +21,7 @@ import { MigrationName1719247426296 } from "./1719247426296-MigrationName"; import { MigrationName1719348009053 } from "./1719348009053-MigrationName"; import { MigrationName1719827175832 } from "./1719827175832-MigrationName"; import { MigrationName1719831213463 } from "./1719831213463-MigrationName"; +import { MigrationName1719838746775 } from "./1719838746775-MigrationName"; export default [ InitialMigration, @@ -46,4 +47,5 @@ export default [ MigrationName1719348009053, MigrationName1719827175832, MigrationName1719831213463, + MigrationName1719838746775 ]; diff --git a/Model/Models/Probe.ts b/Model/Models/Probe.ts index bbe88c7ce3..579953c3ea 100755 --- a/Model/Models/Probe.ts +++ b/Model/Models/Probe.ts @@ -7,6 +7,7 @@ import { PlanType } from "Common/Types/Billing/SubscriptionPlan"; import ColumnAccessControl from "Common/Types/Database/AccessControl/ColumnAccessControl"; import TableAccessControl from "Common/Types/Database/AccessControl/TableAccessControl"; import TableBillingAccessControl from "Common/Types/Database/AccessControl/TableBillingAccessControl"; +import AccessControlColumn from "Common/Types/Database/AccessControlColumn"; import ColumnLength from "Common/Types/Database/ColumnLength"; import ColumnType from "Common/Types/Database/ColumnType"; import CrudApiEndpoint from "Common/Types/Database/CrudApiEndpoint"; @@ -20,7 +21,8 @@ import IconProp from "Common/Types/Icon/IconProp"; import ObjectID from "Common/Types/ObjectID"; import Permission from "Common/Types/Permission"; import Version from "Common/Types/Version"; -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne } from "typeorm"; +import Label from "./Label"; export enum ProbeStatus { Connected = "connected", @@ -36,6 +38,7 @@ export enum ProbeStatus { @IsPermissionsIf(Permission.Public, "projectId", null) @TenantColumn("projectId") @CrudApiEndpoint(new Route("/probe")) +@AccessControlColumn("labels") @SlugifyColumn("name", "slug") @Entity({ name: "Probe", @@ -501,4 +504,52 @@ export default class Probe extends BaseModel { unique: false, }) public connectionStatus?: ProbeStatus = undefined; -} + + + @ColumnAccessControl({ + create: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CreateProjectStatusPage, + ], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.ReadProjectStatusPage, + ], + update: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.EditProjectStatusPage, + ], + }) + @TableColumn({ + required: false, + type: TableColumnType.EntityArray, + modelType: Label, + title: "Labels", + description: + "Relation to Labels Array where this object is categorized in.", + }) + @ManyToMany( + () => { + return Label; + }, + { eager: false }, + ) + @JoinTable({ + name: "ProbeLabel", + inverseJoinColumn: { + name: "labelId", + referencedColumnName: "_id", + }, + joinColumn: { + name: "probeId", + referencedColumnName: "_id", + }, + }) + public labels?: Array