Skip to content

Commit

Permalink
Add first_review_time_seconds metric
Browse files Browse the repository at this point in the history
  • Loading branch information
int128 committed Feb 26, 2022
1 parent 7aa40a4 commit 2a9716e
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 3 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ It has the following tags:
- `base_ref`
- `head_ref`
- `merged` = `true` or `false`
- `first_review_time_seconds`
- Time from the first review request to the first review
- Available if a pull request has both review request and review
- `requested_team`
- Team(s) of requested reviewer(s)
- `label`
Expand Down
2 changes: 1 addition & 1 deletion src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type ClosedPullRequestQueryVariables = Types.Exact<{
}>;


export type ClosedPullRequestQuery = { __typename?: 'Query', repository?: { __typename?: 'Repository', pullRequest?: { __typename?: 'PullRequest', commits: { __typename?: 'PullRequestCommitConnection', nodes?: Array<{ __typename?: 'PullRequestCommit', commit: { __typename?: 'Commit', authoredDate: string, committedDate: string } } | null> | null } } | null } | null };
export type ClosedPullRequestQuery = { __typename?: 'Query', rateLimit?: { __typename?: 'RateLimit', cost: number } | null, repository?: { __typename?: 'Repository', pullRequest?: { __typename?: 'PullRequest', commits: { __typename?: 'PullRequestCommitConnection', nodes?: Array<{ __typename?: 'PullRequestCommit', commit: { __typename?: 'Commit', authoredDate: string, committedDate: string } } | null> | null }, reviewRequests: { __typename?: 'PullRequestTimelineItemsConnection', nodes?: Array<{ __typename?: 'AddedToProjectEvent' } | { __typename?: 'AssignedEvent' } | { __typename?: 'AutoMergeDisabledEvent' } | { __typename?: 'AutoMergeEnabledEvent' } | { __typename?: 'AutoRebaseEnabledEvent' } | { __typename?: 'AutoSquashEnabledEvent' } | { __typename?: 'AutomaticBaseChangeFailedEvent' } | { __typename?: 'AutomaticBaseChangeSucceededEvent' } | { __typename?: 'BaseRefChangedEvent' } | { __typename?: 'BaseRefDeletedEvent' } | { __typename?: 'BaseRefForcePushedEvent' } | { __typename?: 'ClosedEvent' } | { __typename?: 'CommentDeletedEvent' } | { __typename?: 'ConnectedEvent' } | { __typename?: 'ConvertToDraftEvent' } | { __typename?: 'ConvertedNoteToIssueEvent' } | { __typename?: 'CrossReferencedEvent' } | { __typename?: 'DemilestonedEvent' } | { __typename?: 'DeployedEvent' } | { __typename?: 'DeploymentEnvironmentChangedEvent' } | { __typename?: 'DisconnectedEvent' } | { __typename?: 'HeadRefDeletedEvent' } | { __typename?: 'HeadRefForcePushedEvent' } | { __typename?: 'HeadRefRestoredEvent' } | { __typename?: 'IssueComment' } | { __typename?: 'LabeledEvent' } | { __typename?: 'LockedEvent' } | { __typename?: 'MarkedAsDuplicateEvent' } | { __typename?: 'MentionedEvent' } | { __typename?: 'MergedEvent' } | { __typename?: 'MilestonedEvent' } | { __typename?: 'MovedColumnsInProjectEvent' } | { __typename?: 'PinnedEvent' } | { __typename?: 'PullRequestCommit' } | { __typename?: 'PullRequestCommitCommentThread' } | { __typename?: 'PullRequestReview' } | { __typename?: 'PullRequestReviewThread' } | { __typename?: 'PullRequestRevisionMarker' } | { __typename?: 'ReadyForReviewEvent' } | { __typename?: 'ReferencedEvent' } | { __typename?: 'RemovedFromProjectEvent' } | { __typename?: 'RenamedTitleEvent' } | { __typename?: 'ReopenedEvent' } | { __typename?: 'ReviewDismissedEvent' } | { __typename?: 'ReviewRequestRemovedEvent' } | { __typename?: 'ReviewRequestedEvent', createdAt: string } | { __typename?: 'SubscribedEvent' } | { __typename?: 'TransferredEvent' } | { __typename?: 'UnassignedEvent' } | { __typename?: 'UnlabeledEvent' } | { __typename?: 'UnlockedEvent' } | { __typename?: 'UnmarkedAsDuplicateEvent' } | { __typename?: 'UnpinnedEvent' } | { __typename?: 'UnsubscribedEvent' } | { __typename?: 'UserBlockedEvent' } | null> | null }, reviews: { __typename?: 'PullRequestTimelineItemsConnection', nodes?: Array<{ __typename?: 'AddedToProjectEvent' } | { __typename?: 'AssignedEvent' } | { __typename?: 'AutoMergeDisabledEvent' } | { __typename?: 'AutoMergeEnabledEvent' } | { __typename?: 'AutoRebaseEnabledEvent' } | { __typename?: 'AutoSquashEnabledEvent' } | { __typename?: 'AutomaticBaseChangeFailedEvent' } | { __typename?: 'AutomaticBaseChangeSucceededEvent' } | { __typename?: 'BaseRefChangedEvent' } | { __typename?: 'BaseRefDeletedEvent' } | { __typename?: 'BaseRefForcePushedEvent' } | { __typename?: 'ClosedEvent' } | { __typename?: 'CommentDeletedEvent' } | { __typename?: 'ConnectedEvent' } | { __typename?: 'ConvertToDraftEvent' } | { __typename?: 'ConvertedNoteToIssueEvent' } | { __typename?: 'CrossReferencedEvent' } | { __typename?: 'DemilestonedEvent' } | { __typename?: 'DeployedEvent' } | { __typename?: 'DeploymentEnvironmentChangedEvent' } | { __typename?: 'DisconnectedEvent' } | { __typename?: 'HeadRefDeletedEvent' } | { __typename?: 'HeadRefForcePushedEvent' } | { __typename?: 'HeadRefRestoredEvent' } | { __typename?: 'IssueComment' } | { __typename?: 'LabeledEvent' } | { __typename?: 'LockedEvent' } | { __typename?: 'MarkedAsDuplicateEvent' } | { __typename?: 'MentionedEvent' } | { __typename?: 'MergedEvent' } | { __typename?: 'MilestonedEvent' } | { __typename?: 'MovedColumnsInProjectEvent' } | { __typename?: 'PinnedEvent' } | { __typename?: 'PullRequestCommit' } | { __typename?: 'PullRequestCommitCommentThread' } | { __typename?: 'PullRequestReview', createdAt: string, author?: { __typename?: 'Bot', login: string } | { __typename?: 'EnterpriseUserAccount', login: string } | { __typename?: 'Mannequin', login: string } | { __typename?: 'Organization', login: string } | { __typename?: 'User', login: string } | null } | { __typename?: 'PullRequestReviewThread' } | { __typename?: 'PullRequestRevisionMarker' } | { __typename?: 'ReadyForReviewEvent' } | { __typename?: 'ReferencedEvent' } | { __typename?: 'RemovedFromProjectEvent' } | { __typename?: 'RenamedTitleEvent' } | { __typename?: 'ReopenedEvent' } | { __typename?: 'ReviewDismissedEvent' } | { __typename?: 'ReviewRequestRemovedEvent' } | { __typename?: 'ReviewRequestedEvent' } | { __typename?: 'SubscribedEvent' } | { __typename?: 'TransferredEvent' } | { __typename?: 'UnassignedEvent' } | { __typename?: 'UnlabeledEvent' } | { __typename?: 'UnlockedEvent' } | { __typename?: 'UnmarkedAsDuplicateEvent' } | { __typename?: 'UnpinnedEvent' } | { __typename?: 'UnsubscribedEvent' } | { __typename?: 'UserBlockedEvent' } | null> | null } } | null } | null };

export type CompletedCheckSuiteQueryVariables = Types.Exact<{
node_id: Types.Scalars['ID'];
Expand Down
12 changes: 12 additions & 0 deletions src/pullRequest/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,18 @@ export const computePullRequestClosedMetrics = (
points: [[t, t - unixTime(pr.firstCommit.committedDate)]],
}
)

if (pr.firstReviewRequest && pr.firstReview) {
const firstReviewRequestedAt = unixTime(pr.firstReviewRequest.createdAt)
const firstReviewedAt = unixTime(pr.firstReview.createdAt)
series.push({
host: 'github.com',
tags,
metric: 'github.actions.pull_request_closed.first_review_time_seconds',
type: 'gauge',
points: [[t, firstReviewedAt - firstReviewRequestedAt]],
})
}
}

// Datadog treats a tag as combination of values.
Expand Down
56 changes: 54 additions & 2 deletions src/queries/closedPullRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { Octokit } from '../types'

const query = /* GraphQL */ `
query closedPullRequest($owner: String!, $name: String!, $number: Int!) {
rateLimit {
cost
}
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
commits(first: 1) {
Expand All @@ -13,6 +16,22 @@ const query = /* GraphQL */ `
}
}
}
reviewRequests: timelineItems(itemTypes: [REVIEW_REQUESTED_EVENT], first: 1) {
nodes {
__typename
... on ReviewRequestedEvent {
createdAt
}
}
}
reviews: timelineItems(itemTypes: [PULL_REQUEST_REVIEW], first: 1) {
nodes {
__typename
... on PullRequestReview {
createdAt
}
}
}
}
}
}
Expand All @@ -23,6 +42,12 @@ export type ClosedPullRequest = {
authoredDate: string
committedDate: string
}
firstReviewRequest?: {
createdAt: string
}
firstReview?: {
createdAt: string
}
}

export const queryClosedPullRequest = async (
Expand All @@ -36,8 +61,35 @@ export const queryClosedPullRequest = async (
if (r.repository.pullRequest.commits.nodes[0] == null) {
throw new Error(`commit is null: ${JSON.stringify(r)}`)
}
const firstCommit = r.repository.pullRequest.commits.nodes[0].commit
return {
firstCommit,
firstCommit: r.repository.pullRequest.commits.nodes[0].commit,
...findFirstReviewRequest(r),
...findFirstReview(r),
}
}

const findFirstReviewRequest = (
r: ClosedPullRequestQuery
): Pick<ClosedPullRequest, 'firstReviewRequest'> | undefined => {
if (!r.repository?.pullRequest?.reviewRequests.nodes?.length) {
return undefined
}
if (r.repository.pullRequest.reviewRequests.nodes[0]?.__typename !== 'ReviewRequestedEvent') {
return undefined
}
return {
firstReviewRequest: r.repository.pullRequest.reviewRequests.nodes[0],
}
}

const findFirstReview = (r: ClosedPullRequestQuery): Pick<ClosedPullRequest, 'firstReview'> | undefined => {
if (!r.repository?.pullRequest?.reviews.nodes?.length) {
return undefined
}
if (r.repository.pullRequest.reviews.nodes[0]?.__typename !== 'PullRequestReview') {
return undefined
}
return {
firstReview: r.repository.pullRequest.reviews.nodes[0],
}
}
23 changes: 23 additions & 0 deletions tests/__snapshots__/run.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,29 @@ Array [
],
"type": "gauge",
},
Object {
"host": "github.com",
"metric": "github.actions.pull_request_closed.first_review_time_seconds",
"points": Array [
Array [
1558279233,
600,
],
],
"tags": Array [
"repository_owner:Codertocat",
"repository_name:Hello-World",
"sender:Codertocat",
"sender_type:User",
"user:Codertocat",
"pull_request_number:2",
"draft:false",
"base_ref:master",
"head_ref:changes",
"merged:false",
],
"type": "gauge",
},
Object {
"host": "github.com",
"metric": "github.actions.api_rate_limit.remaining",
Expand Down
16 changes: 16 additions & 0 deletions tests/pullRequest/fixtures/closedPullRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ export const exampleClosedPullRequestQuery: ClosedPullRequestQuery = {
},
],
},
reviewRequests: {
nodes: [
{
__typename: 'ReviewRequestedEvent',
createdAt: '2019-05-15T15:30:00Z',
},
],
},
reviews: {
nodes: [
{
__typename: 'PullRequestReview',
createdAt: '2019-05-15T15:40:00Z',
},
],
},
},
},
}
Expand Down

0 comments on commit 2a9716e

Please sign in to comment.