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

Add [Coderabbit] PR Stats service and tests #10749

Merged
merged 14 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
68 changes: 68 additions & 0 deletions services/coderabbit/coderabbit-stats.service.js
jNullj marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Joi from 'joi'
import { BaseJsonService, pathParams } from '../index.js'

const schema = Joi.object({
reviews: Joi.number().required(),
}).required()

class CodeRabbitStats extends BaseJsonService {
static category = 'analysis'
static route = {
base: 'coderabbit',
pattern: 'prs/:provider/:org/:repo',
}

static openApi = {
'/coderabbit/prs/{provider}/{org}/{repo}': {
get: {
summary: 'CodeRabbit Pull Request Reviews',
description:
'This badge pulls the number of PRs reviewed by [CodeRabbit](https://coderabbit.ai), AI code review tool',
parameters: pathParams(
{
name: 'provider',
example: 'github, gitlab, bitbucket',
description: 'Version Control Provider (e.g., github)',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example value should be a value that works, so "github" would be sensible value for example here. Basically if I copy and paste it into the builder, it should work.

We also don't need to double up examples in the description.

In the docs, if we want to communicate to the user that there is a closed set of valid values, we can give the user a dropdown to pick from. So if you look at something like the vcsType param on https://shields.io/badges/coveralls you get a dropdown for [github, bitbucket, gitlab].

Screenshot at 2024-12-23 18-12-35

Here's what that looks like in the code.

pattern: ':vcsType(github|bitbucket|gitlab)/:user/:repo+',

pathParam({
name: 'vcsType',
example: 'github',
schema: { type: 'string', enum: this.getEnum('vcsType') },
}),

Lets do the same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok for the time-being it showing only GH. As GH has most usage, I'll be happy to update in the next revision with the upcoming badges.

Editing the text to only show GitHub.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Are gitlab and bitbucket values that work today, or not?

If they are, lets make this

static route = {
  base: 'coderabbit',
  pattern: 'prs/:provider(github|bitbucket|gitlab)/:org/:repo',
}

in the route and

{
  name: 'provider',
  example: 'github',
  description: 'Version Control Provider',
  schema: { type: 'string', enum: this.getEnum('provider') },
}

in the Open API spec.

That will show it as

Screenshot at 2024-12-23 20-21-57

in the docs, and we're good to go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this change as you mentioned. Thanks for the suggestion!

},
{
name: 'org',
example: 'coderabbitai',
description: 'Organization or User name',
},
{
name: 'repo',
example: 'ast-grep-essentials',
description: 'Repository name',
},
),
},
},
}

static defaultBadgeData = {
label: 'coderabbit reviews',
}

static render({ reviews }) {
return {
message: `${reviews}`,
}
aravindputrevu marked this conversation as resolved.
Show resolved Hide resolved
}

async fetch({ provider, org, repo }) {
return this._requestJson({
schema,
url: `https://api.coderabbit.ai/stats/${provider}/${org}/${repo}`,
httpErrors: {
404: 'repo not found',
},
chris48s marked this conversation as resolved.
Show resolved Hide resolved
})
}

async handle({ provider, org, repo }) {
const data = await this.fetch({ provider, org, repo })
return this.constructor.render(data)
}
}

export default CodeRabbitStats
32 changes: 32 additions & 0 deletions services/coderabbit/coderabbit-stats.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'

export const t = await createServiceTester()

t.create('live CodeRabbitStats')
.get('/prs/github/coderabbitai/ast-grep-essentials.json')
.expectBadge({
label: 'coderabbit reviews',
message: Joi.number().min(0),
})

t.create('live CodeRabbitStats demo repo')
.get('/prs/github/coderabbitai/coderabbit-docs.json')
.expectBadge({
label: 'coderabbit reviews',
message: Joi.number().min(0),
})

jNullj marked this conversation as resolved.
Show resolved Hide resolved
t.create('live CodeRabbitStats nonexistent org')
.get('/prs/github/not-valid/not-found.json')
.expectBadge({
label: 'coderabbit reviews',
message: 'invalid',
})

t.create('live CodeRabbitStats invalid repo')
.get('/prs/github/coderabbitai/invalid-repo-name.json')
.expectBadge({
label: 'coderabbit reviews',
message: 'invalid',
})
Loading