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

Send routing and triage timestamps for Waiting for: * labeling actions to BigQuery #457

Merged
merged 1 commit into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/utils/businessHours.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
MAX_TRIAGE_DAYS,
UNROUTED_LABEL,
UNTRIAGED_LABEL,
WAITING_FOR_PRODUCT_OWNER_LABEL,
WAITING_FOR_SUPPORT_LABEL,
} from '@/config';
import { bolt } from '@api/slack';
import { db } from '@utils/db';
Expand Down Expand Up @@ -269,6 +271,11 @@ describe('businessHours tests', function () {
expect(result).toEqual(null);
});

it('should not calculate SLO violation if label is waiting for product owner', async function () {
const result = await calculateSLOViolationRoute(WAITING_FOR_PRODUCT_OWNER_LABEL);
expect(result).toEqual(null);
});

it('should calculate SLO violation if label is unrouted', async function () {
const result = await calculateSLOViolationRoute(UNROUTED_LABEL);
expect(result).not.toEqual(null);
Expand Down Expand Up @@ -396,10 +403,25 @@ describe('businessHours tests', function () {
expect(result).not.toEqual(null);
});

it('should calculate SLO violation if label is assigned to another product area', async function () {
it('should calculate SLO violation if label is waiting for product owner', async function () {
const result = await calculateSLOViolationTriage(WAITING_FOR_PRODUCT_OWNER_LABEL, [
{ name: 'Product Area: Test' },
]);
expect(result).not.toEqual(null);
});

it('should calculate SLO violation if label is assigned to another product area for untriaged label', async function () {
const result = await calculateSLOViolationTriage(
'Product Area: Rerouted',
[{ name: UNTRIAGED_LABEL }]
);
expect(result).not.toEqual(null);
});

it('should calculate SLO violation if label is assigned to another product area for waiting for product owner label', async function () {
const result = await calculateSLOViolationTriage(
'Product Area: Rerouted',
[{ name: 'Status: Untriaged' }]
[{ name: WAITING_FOR_PRODUCT_OWNER_LABEL }]
);
expect(result).not.toEqual(null);
});
Expand Down
10 changes: 6 additions & 4 deletions src/utils/businessHours.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
TEAM_OSPO_CHANNEL_ID,
UNROUTED_LABEL,
UNTRIAGED_LABEL,
WAITING_FOR_PRODUCT_OWNER_LABEL,
WAITING_FOR_SUPPORT_LABEL,
} from '@/config';
import { bolt } from '@api/slack';

Expand Down Expand Up @@ -52,7 +54,7 @@ export async function calculateTimeToRespondBy(

export async function calculateSLOViolationTriage(target_name, labels) {
// calculate time to triage for issues that come in with untriaged label
if (target_name === UNTRIAGED_LABEL) {
if (target_name === UNTRIAGED_LABEL || target_name === WAITING_FOR_PRODUCT_OWNER_LABEL) {
const productArea = labels?.find((label) =>
label.name.startsWith(PRODUCT_AREA_LABEL_PREFIX)
)?.name;
Expand All @@ -61,15 +63,15 @@ export async function calculateSLOViolationTriage(target_name, labels) {
// calculate time to triage for issues that are rerouted
else if (
target_name.startsWith(PRODUCT_AREA_LABEL_PREFIX) &&
labels?.some((label) => label.name === UNTRIAGED_LABEL)
labels?.some((label) => label.name === UNTRIAGED_LABEL || label.name === WAITING_FOR_PRODUCT_OWNER_LABEL)
) {
return calculateTimeToRespondBy(MAX_TRIAGE_DAYS, target_name);
}
return null;
}

export async function calculateSLOViolationRoute(target_name) {
if (target_name === UNROUTED_LABEL) {
if (target_name === UNROUTED_LABEL || target_name === WAITING_FOR_SUPPORT_LABEL) {
return calculateTimeToRespondBy(MAX_ROUTE_DAYS, 'Product Area: Unknown');
}
return null;
Expand Down Expand Up @@ -157,7 +159,7 @@ export async function getNextAvailableBusinessHourWindow(
if (offices.length === 0) {
offices = await getOffices('Product Area: Other');
if (offices.length === 0) {
throw new Error('Open Source productArea not subscribed to any offices.');
throw new Error('Other productArea not subscribed to any offices.');
}
}
const businessHourWindows: BusinessHourWindow[] = [];
Expand Down
75 changes: 72 additions & 3 deletions src/utils/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ jest.mock('@google-cloud/bigquery', () => ({
}));

import { getLabelsTable } from '@/brain/issueNotifier';
import { UNROUTED_LABEL, UNTRIAGED_LABEL } from '@/config';
import {
UNROUTED_LABEL,
UNTRIAGED_LABEL,
WAITING_FOR_PRODUCT_OWNER_LABEL,
WAITING_FOR_SUPPORT_LABEL,
WAITING_FOR_COMMUNITY_LABEL
} from '@/config';
import { db } from '@utils/db';

import { insertOss } from './metrics';
Expand Down Expand Up @@ -81,6 +87,16 @@ describe('metrics tests', function () {
});
});

it('should calculate triage by timestamp if labeled waiting for product owner', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_PRODUCT_OWNER_LABEL;
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: null,
timeToTriageBy: '2017-02-16T01:00:00.000Z',
});
});

it('should calculate route by timestamp if labeled unrouted status', async function () {
const testPayload = defaultPayload;
testPayload.label.name = UNROUTED_LABEL;
Expand All @@ -91,7 +107,60 @@ describe('metrics tests', function () {
});
});

it('should not calculate route by timestamp if unlabeled untriaged status', async function () {
it('should calculate route by timestamp if labeled waiting for support', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_SUPPORT_LABEL;
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: '2017-02-15T01:00:00.000Z',
timeToTriageBy: null,
});
});

it('should not calculate timestamps if labeled waiting for product community', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_COMMUNITY_LABEL;
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: null,
timeToTriageBy: null,
});
});

it('should not calculate timestamps if unlabeled waiting for product community', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_COMMUNITY_LABEL;
testPayload.action = 'unlabeled';
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: null,
timeToTriageBy: null,
});
});

it('should not calculate timestamps if unlabeled waiting for product owner', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_PRODUCT_OWNER_LABEL;
testPayload.action = 'unlabeled';
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: null,
timeToTriageBy: null,
});
});

it('should not calculate timestamps if unlabeled waiting for support', async function () {
const testPayload = defaultPayload;
testPayload.label.name = WAITING_FOR_SUPPORT_LABEL;
testPayload.action = 'unlabeled';
const result = await insertOss('issues', testPayload);
expect(result).toMatchObject({
timeToRouteBy: null,
timeToTriageBy: null,
});
});

it('should not calculate timestamps if unlabeled untriaged status', async function () {
const testPayload = defaultPayload;
testPayload.label.name = UNTRIAGED_LABEL;
testPayload.action = 'unlabeled';
Expand All @@ -102,7 +171,7 @@ describe('metrics tests', function () {
});
});

it('should not calculate route by timestamp if unlabeled unrouted status', async function () {
it('should not calculate timestamps if unlabeled unrouted status', async function () {
const testPayload = defaultPayload;
testPayload.label.name = UNROUTED_LABEL;
testPayload.action = 'unlabeled';
Expand Down