mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
196 lines
5.0 KiB
TypeScript
196 lines
5.0 KiB
TypeScript
import CreateBy from "../Types/Database/CreateBy";
|
|
import DeleteBy from "../Types/Database/DeleteBy";
|
|
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
import DatabaseService from "./DatabaseService";
|
|
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
import Model from "../../Models/DatabaseModels/Team";
|
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
import ObjectID from "../../Types/ObjectID";
|
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
import TeamMember from "../../Models/DatabaseModels/TeamMember";
|
|
import TeamMemberService from "./TeamMemberService";
|
|
import ProjectSCIMService from "./ProjectSCIMService";
|
|
|
|
export class Service extends DatabaseService<Model> {
|
|
public constructor() {
|
|
super(Model);
|
|
}
|
|
|
|
public async getTeamsUserIsAPartOf(data: {
|
|
userId: ObjectID;
|
|
projectId: ObjectID;
|
|
}): Promise<Array<Model>> {
|
|
const teamMembers: Array<TeamMember> = await TeamMemberService.findBy({
|
|
query: {
|
|
userId: data.userId,
|
|
projectId: data.projectId,
|
|
},
|
|
select: {
|
|
team: {
|
|
name: true,
|
|
_id: true,
|
|
},
|
|
},
|
|
limit: LIMIT_MAX,
|
|
skip: 0,
|
|
props: {
|
|
isRoot: true,
|
|
},
|
|
});
|
|
|
|
const teams: Array<Model> = [];
|
|
|
|
for (const teamMember of teamMembers) {
|
|
if (teamMember.team) {
|
|
teams.push(teamMember.team);
|
|
}
|
|
}
|
|
|
|
return teams;
|
|
}
|
|
|
|
private async assertScimAllowsTeamMutation(data: {
|
|
projectIds: Array<ObjectID>;
|
|
action: "create" | "delete";
|
|
}): Promise<void> {
|
|
if (!data.projectIds || data.projectIds.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const uniqueProjectIds: Map<string, ObjectID> = new Map();
|
|
|
|
for (const projectId of data.projectIds) {
|
|
if (projectId) {
|
|
uniqueProjectIds.set(projectId.toString(), new ObjectID(projectId));
|
|
}
|
|
}
|
|
|
|
for (const projectId of uniqueProjectIds.values()) {
|
|
const scimCount: PositiveNumber = await ProjectSCIMService.countBy({
|
|
query: {
|
|
projectId: projectId,
|
|
enablePushGroups: true,
|
|
},
|
|
skip: new PositiveNumber(0),
|
|
limit: new PositiveNumber(1),
|
|
props: {
|
|
isRoot: true,
|
|
tenantId: projectId,
|
|
},
|
|
});
|
|
|
|
if (scimCount.toNumber() > 0) {
|
|
throw new BadDataException(
|
|
`Cannot ${data.action} teams while SCIM Push Groups is enabled for this project. Disable Push Groups to manage teams from OneUptime.`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
@CaptureSpan()
|
|
protected override async onBeforeCreate(
|
|
createBy: CreateBy<Model>,
|
|
): Promise<OnCreate<Model>> {
|
|
let projectId: ObjectID | undefined = createBy.data.projectId;
|
|
|
|
if (!projectId && createBy.props.tenantId) {
|
|
projectId = new ObjectID(createBy.props.tenantId);
|
|
}
|
|
|
|
if (!projectId) {
|
|
throw new BadDataException("Project ID cannot be null");
|
|
}
|
|
|
|
projectId = new ObjectID(projectId);
|
|
createBy.data.projectId = projectId;
|
|
|
|
if (!createBy.props.isRoot) {
|
|
await this.assertScimAllowsTeamMutation({
|
|
projectIds: [projectId],
|
|
action: "create",
|
|
});
|
|
}
|
|
|
|
return { createBy, carryForward: null };
|
|
}
|
|
|
|
@CaptureSpan()
|
|
protected override async onBeforeUpdate(
|
|
updateBy: UpdateBy<Model>,
|
|
): Promise<OnUpdate<Model>> {
|
|
// get teams by query.
|
|
|
|
const teams: Array<Model> = await this.findBy({
|
|
query: updateBy.query,
|
|
limit: LIMIT_MAX,
|
|
skip: 0,
|
|
select: {
|
|
name: true,
|
|
isTeamEditable: true,
|
|
},
|
|
|
|
props: updateBy.props,
|
|
});
|
|
|
|
for (const team of teams) {
|
|
if (!team.isTeamEditable) {
|
|
throw new BadDataException(
|
|
`${
|
|
team.name || "This"
|
|
} team cannot be updated because its a critical team for this project.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
return { updateBy, carryForward: null };
|
|
}
|
|
|
|
@CaptureSpan()
|
|
protected override async onBeforeDelete(
|
|
deleteBy: DeleteBy<Model>,
|
|
): Promise<OnDelete<Model>> {
|
|
const teams: Array<Model> = await this.findBy({
|
|
query: deleteBy.query,
|
|
limit: LIMIT_MAX,
|
|
skip: 0,
|
|
select: {
|
|
name: true,
|
|
isTeamDeleteable: true,
|
|
projectId: true,
|
|
},
|
|
|
|
props: deleteBy.props,
|
|
});
|
|
|
|
const projectIds: Array<ObjectID> = teams
|
|
.map((team: Model) => {
|
|
return team.projectId;
|
|
})
|
|
.filter((projectId: ObjectID | undefined): projectId is ObjectID => {
|
|
return Boolean(projectId);
|
|
});
|
|
|
|
if (deleteBy.props.isRoot !== true) {
|
|
await this.assertScimAllowsTeamMutation({
|
|
projectIds: projectIds,
|
|
action: "delete",
|
|
});
|
|
}
|
|
|
|
for (const team of teams) {
|
|
if (!team.isTeamDeleteable) {
|
|
throw new BadDataException(
|
|
`${
|
|
team.name || "This"
|
|
} team cannot be deleted its a critical team for this project.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
return { deleteBy, carryForward: null };
|
|
}
|
|
}
|
|
export default new Service();
|