Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding unique assignment to .eng role #66

Draft
wants to merge 11 commits into
base: trunk
Choose a base branch
from
1 change: 1 addition & 0 deletions config/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export const devConfig: AppConfig = {
breakingRoomPrefix: "breaking-dev-",
commPlatform: devSlackConfig,
priorities: priorityConfig,
breakingAllowMultiRoles: false,
runbookRootUrl: "https://automattic.com",
} as const;
1 change: 1 addition & 0 deletions config/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const testConfig: AppConfig = {
breakingMainRoom: "unit-test-breaking-main",
breakingNotifyRoom: "unit-test-breaking-notify",
breakingRoomPrefix: "unit-test-breaking-",
breakingAllowMultiRoles: true,
commPlatform: testSlackConfig,
priorities: priorityConfig,
runbookRootUrl: "https://automattic.com",
Expand Down
1 change: 1 addition & 0 deletions config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type AppConfig = {
breakingMainRoom: string;
breakingNotifyRoom?: string;
breakingRoomPrefix: string;
breakingAllowMultiRoles: boolean;
commPlatform: CommPlatformConfig;
componentListUrl?: string;
issueTracker?: IssueTrackerConfig;
Expand Down
200 changes: 200 additions & 0 deletions lib/boundary/hubot/handlers/__tests__/incident.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,56 @@ describe("incident.ts", () => {
expect(robot.incidents[TEST_ROOM].data().point).toBeNull();
expect(robot.incidents[TEST_ROOM].data().acknowledgedAt).toBeNull();
});

test("fails multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = false;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
comms: "adele",
point: null,
}),
);

setUserCacheEntry(robot.users, "adele");
mockFluentDbUpdateOnce(robot, [{ point: "adele" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetPoint(robot, TEST_ROOM, "adele", "adele");
expect(robot.incidents[TEST_ROOM].data().point).toBeNull();
expect(robot.adapter.replyToMessage).toHaveBeenCalledTimes(1);
expect(robot.adapter.replyToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"Sorry, you're already assigned to comms!",
undefined,
);
expect(robot.adapter.reactToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"no_entry",
undefined,
);
});

test("success with multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = true;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
comms: "adele",
point: null,
}),
);

setUserCacheEntry(robot.users, "adele");
mockFluentDbUpdateOnce(robot, [{ point: "adele" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetPoint(robot, TEST_ROOM, "adele", "adele");
expect(robot.incidents[TEST_ROOM].data().point).not.toBeNull();
expect(robot.incidents[TEST_ROOM].data().point).toBe("adele");
});
});

describe("incidentSetComms", () => {
Expand Down Expand Up @@ -665,6 +715,56 @@ describe("incident.ts", () => {
expect(robot.incidents[TEST_ROOM].data().comms).toBeNull();
expect(robot.incidents[TEST_ROOM].data().acknowledgedAt).toBeNull();
});

test("fails multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = false;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
triage: "adele",
comms: null,
}),
);

setUserCacheEntry(robot.users, "adele");
mockFluentDbUpdateOnce(robot, [{ comms: "adele" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetComms(robot, TEST_ROOM, "adele", "adele");
expect(robot.incidents[TEST_ROOM].data().comms).toBeNull();
expect(robot.adapter.replyToMessage).toHaveBeenCalledTimes(1);
expect(robot.adapter.replyToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"Sorry, you're already assigned to triage!",
undefined,
);
expect(robot.adapter.reactToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"no_entry",
undefined,
);
});

test("success with multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = true;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
point: "adele",
comms: null,
}),
);

setUserCacheEntry(robot.users, "adele");
mockFluentDbUpdateOnce(robot, [{ comms: "adele" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetComms(robot, TEST_ROOM, "adele", "adele");
expect(robot.incidents[TEST_ROOM].data().comms).not.toBeNull();
expect(robot.incidents[TEST_ROOM].data().comms).toBe("adele");
});
});

describe("incidentSetTriage", () => {
Expand Down Expand Up @@ -722,6 +822,56 @@ describe("incident.ts", () => {
expect(robot.db.insert).toHaveBeenCalledTimes(0);
expect(robot.incidents[TEST_ROOM].data().triage).toBeNull();
});

test("fails multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = false;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
engLead: "stani",
triage: null,
}),
);

setUserCacheEntry(robot.users, "stani");
mockFluentDbUpdateOnce(robot, [{ triage: "stani" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetTriage(robot, TEST_ROOM, "stani", "stani");
expect(robot.incidents[TEST_ROOM].data().triage).toBeNull();
expect(robot.adapter.replyToMessage).toHaveBeenCalledTimes(1);
expect(robot.adapter.replyToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"Sorry, you're already assigned to engLead!",
undefined,
);
expect(robot.adapter.reactToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"no_entry",
undefined,
);
});

test("success with multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = true;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
engLead: "stani",
triage: null,
}),
);

setUserCacheEntry(robot.users, "stani");
mockFluentDbUpdateOnce(robot, [{ triage: "stani" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetTriage(robot, TEST_ROOM, "stani", "stani");
expect(robot.incidents[TEST_ROOM].data().triage).not.toBeNull();
expect(robot.incidents[TEST_ROOM].data().triage).toBe("stani");
});
});

describe("incidentSetEngLead", () => {
Expand Down Expand Up @@ -779,6 +929,56 @@ describe("incident.ts", () => {
expect(robot.db.insert).toHaveBeenCalledTimes(0);
expect(robot.incidents[TEST_ROOM].data().engLead).toBeNull();
});

test("fails multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = false;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
point: "luke",
engLead: null,
}),
);

setUserCacheEntry(robot.users, "luke");
mockFluentDbUpdateOnce(robot, [{ engLead: "luke" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetEngLead(robot, TEST_ROOM, "luke", "luke");
expect(robot.incidents[TEST_ROOM].data().engLead).toBeNull();
expect(robot.adapter.replyToMessage).toHaveBeenCalledTimes(1);
expect(robot.adapter.replyToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"Sorry, you're already assigned to point!",
undefined,
);
expect(robot.adapter.reactToMessage).toHaveBeenCalledWith(
TEST_ROOM,
"no_entry",
undefined,
);
});

test("success with multi-role assignment", async () => {
robot.config.breakingAllowMultiRoles = true;

// @ts-expect-error
robot.incidents[TEST_ROOM] = newIncidentMachine(
createIncident({
point: "luke",
engLead: null,
}),
);

setUserCacheEntry(robot.users, "luke");
mockFluentDbUpdateOnce(robot, [{ engLead: "luke" }]);
mockFluentDbInsertOnce(robot, []);

await incidentSetEngLead(robot, TEST_ROOM, "luke", "luke");
expect(robot.incidents[TEST_ROOM].data().engLead).not.toBeNull();
expect(robot.incidents[TEST_ROOM].data().engLead).toBe("luke");
});
});

describe("incidentSetSummary", () => {
Expand Down
92 changes: 92 additions & 0 deletions lib/boundary/hubot/handlers/incident.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,16 @@ export const incidentSetPoint = async (
) => {
const incident = robot.incidents[room].data();

if (
robot.config.breakingAllowMultiRoles === false &&
isUserAlreadyInRole(incident, point)
) {
const tasks = getUserAlreadyInRoleErrorMsg(robot, room, point, messageId);
if (tasks.length) {
return Promise.allSettled(tasks);
}
}

if (point === incident.point) {
return robot.adapter.reactToMessage(room, "ok_hand", messageId);
}
Expand Down Expand Up @@ -511,6 +521,21 @@ export const incidentSetComms = async (
) => {
const incident = robot.incidents[room].data();

console.log(">>>>> 1");
katag9k marked this conversation as resolved.
Show resolved Hide resolved

if (
robot.config.breakingAllowMultiRoles === false &&
isUserAlreadyInRole(incident, comms)
) {
const tasks = getUserAlreadyInRoleErrorMsg(robot, room, comms, messageId);
if (tasks.length) {
console.log(">>>>> 2");
katag9k marked this conversation as resolved.
Show resolved Hide resolved
return Promise.allSettled(tasks);
}
}

console.log(">>>>> 3");
katag9k marked this conversation as resolved.
Show resolved Hide resolved

if (comms === incident.comms) {
return robot.adapter.reactToMessage(room, "ok_hand", messageId);
}
Expand Down Expand Up @@ -562,6 +587,16 @@ export const incidentSetTriage = async (
) => {
const incident = robot.incidents[room].data();

if (
robot.config.breakingAllowMultiRoles === false &&
isUserAlreadyInRole(incident, triage)
) {
const tasks = getUserAlreadyInRoleErrorMsg(robot, room, triage, messageId);
if (tasks.length) {
return Promise.allSettled(tasks);
}
}

if (triage === incident.triage) {
return robot.adapter.reactToMessage(room, "ok_hand", messageId);
}
Expand Down Expand Up @@ -609,6 +644,16 @@ export const incidentSetEngLead = async (
) => {
const incident = robot.incidents[room].data();

if (
robot.config.breakingAllowMultiRoles === false &&
isUserAlreadyInRole(incident, engLead)
) {
const tasks = getUserAlreadyInRoleErrorMsg(robot, room, engLead, messageId);
if (tasks.length) {
return Promise.allSettled(tasks);
}
}

if (engLead === incident.engLead) {
return robot.adapter.reactToMessage(room, "ok_hand", messageId);
}
Expand Down Expand Up @@ -1386,3 +1431,50 @@ const permalink = async (
) => {
return messageId ? await adapter.getPermalink(room, messageId) : undefined;
};

export const isUserAlreadyInRole = (incident: Incident, user: string) => {
return [
incident.comms,
incident.point,
incident.engLead,
incident.triage,
].includes(user);
};

export const getUserAlreadyInRoleErrorMsg = (
robot: BreakingBot,
room: string,
user: string,
messageId?: string,
) => {
let currentRole = null;
const incident = robot.incidents[room].data();
const roles = {
comms: incident.comms,
point: incident.point,
engLead: incident.engLead,
triage: incident.triage,
};

for (const [role, roleUser] of Object.entries(roles)) {
if (user === roleUser) {
currentRole = role;
break;
}
}

if (currentRole) {
const tasks = [];
tasks.push(
robot.adapter.replyToMessage(
room,
`Sorry, you're already assigned to ${currentRole}!`,
messageId,
),
);
tasks.push(robot.adapter.reactToMessage(room, "no_entry", messageId));
return tasks;
}

return [];
};