Skip to content
This repository was archived by the owner on Apr 3, 2023. It is now read-only.

Commit b2debd4

Browse files
0xPoeti-chi-bot
andauthored
feat: add contributors API (#166)
* feat: add contributors API * feat: add total number * test: add api cases * test: add service cases * docs: add comment Co-authored-by: Ti Prow Robot <71242396+ti-community-prow-bot@users.noreply.github.com>
1 parent 94e044a commit b2debd4

File tree

11 files changed

+454
-1
lines changed

11 files changed

+454
-1
lines changed

src/api/contributor/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Request, Response } from "express";
2+
3+
import { IContributorService } from "../../services/contributor";
4+
import { StatusCodes } from "http-status-codes";
5+
import { Response as Res } from "../../services/response";
6+
import { ContributorMessage } from "../../services/messages/ContributorMessage";
7+
8+
/**
9+
* List contributors.
10+
* @param req
11+
* @param res
12+
* @param contributorService
13+
*/
14+
export async function listContributors(
15+
req: Request,
16+
res: Response,
17+
contributorService: IContributorService
18+
) {
19+
// Gather paginate query.
20+
const { current, pageSize } = req.query;
21+
let paginateQuery;
22+
23+
// Validate paginate query.
24+
if (current !== undefined && pageSize !== undefined) {
25+
const currentNum = Number(current);
26+
const pageSizeNum = Number(pageSize);
27+
if (!Number.isInteger(currentNum) || !Number.isInteger(pageSizeNum)) {
28+
const response: Res<null> = {
29+
data: null,
30+
status: StatusCodes.BAD_REQUEST,
31+
message: ContributorMessage.IllegalQueryParameters,
32+
};
33+
34+
res.status(response.status);
35+
res.json(response);
36+
return;
37+
}
38+
paginateQuery = {
39+
current: currentNum,
40+
pageSize: pageSizeNum,
41+
};
42+
}
43+
44+
const response = await contributorService.listContributors(paginateQuery);
45+
46+
res.status(response.status);
47+
res.json(response);
48+
}

src/db/entities/Pull.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Entity, Index, PrimaryGeneratedColumn, Column } from "typeorm";
2+
3+
@Entity({ name: "pulls" })
4+
export class Pull {
5+
@PrimaryGeneratedColumn({ name: "id" })
6+
id: number;
7+
8+
@Column({ default: null })
9+
owner: string;
10+
11+
@Column({ default: null })
12+
repo: string;
13+
14+
@Column({ name: "pull_number", default: null })
15+
@Index()
16+
pullNumber: number;
17+
18+
@Column({ type: "text" })
19+
title: string;
20+
21+
@Column({ type: "text" })
22+
body: string;
23+
24+
@Column({ default: null })
25+
user: string;
26+
27+
@Column({ default: null })
28+
association: string;
29+
30+
@Column({ default: null })
31+
relation: string;
32+
33+
@Column({ type: "text" })
34+
label: string;
35+
36+
@Column({ type: "varchar", length: 128 })
37+
status: string;
38+
39+
// FIXME: maybe this means issue created time.
40+
@Column({
41+
name: "created_at",
42+
type: "timestamp",
43+
nullable: false,
44+
default: () => "CURRENT_TIMESTAMP",
45+
})
46+
createdAt: string;
47+
48+
@Column({
49+
name: "updated_at",
50+
type: "timestamp",
51+
nullable: true,
52+
default: null,
53+
})
54+
updatedAt: string;
55+
56+
@Column({
57+
name: "closed_at",
58+
type: "timestamp",
59+
nullable: true,
60+
default: null,
61+
})
62+
closedAt: string;
63+
64+
@Column({
65+
name: "merged_at",
66+
type: "timestamp",
67+
nullable: true,
68+
default: null,
69+
})
70+
mergedAt: string;
71+
}

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { getSig } from "./api/sig";
1111
import { listOwners } from "./api/pull";
1212
import { handlePullRequestEvents } from "./events/pull";
1313
import { Router } from "express";
14+
import { listContributors } from "./api/contributor";
15+
import ContributorService from "./services/contributor";
1416

1517
const commands = require("probot-commands-pro");
1618
const bodyParser = require("body-parser");
@@ -90,6 +92,10 @@ export = (
9092
router.get("/sigs/:name", async (req, res) => {
9193
await getSig(req, res, Container.get(SigService));
9294
});
95+
96+
router.get("/contributors", async (req, res) => {
97+
await listContributors(req, res, Container.get(ContributorService));
98+
});
9399
})
94100
.catch((err) => {
95101
app.log.fatal("Connect to db failed", err);

src/queries/PaginateQuery.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Paginate query.
3+
*/
4+
export interface PaginateQuery {
5+
/**
6+
* Current page.
7+
* Start with 1.
8+
*/
9+
current: number;
10+
/**
11+
* Size of each page.
12+
*/
13+
pageSize: number;
14+
}

src/repositoies/contributor/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { EntityRepository } from "typeorm";
2+
import { Service } from "typedi";
3+
import { Repository } from "typeorm/repository/Repository";
4+
5+
import { Pull } from "../../db/entities/Pull";
6+
7+
@Service()
8+
@EntityRepository(Pull)
9+
export default class ContributorRepository extends Repository<Pull> {
10+
/**
11+
* List contributors.
12+
*/
13+
public async listContributors(
14+
skip?: number,
15+
take?: number
16+
): Promise<string[]> {
17+
return (
18+
await this.createQueryBuilder()
19+
.select("distinct user as githubName")
20+
.skip(skip)
21+
.take(take)
22+
.getRawMany()
23+
).map((c) => {
24+
return c.githubName;
25+
});
26+
}
27+
28+
public async getContributorsCount(): Promise<number> {
29+
return (
30+
await this.createQueryBuilder()
31+
.select("count(distinct user) as total")
32+
.getRawOne()
33+
).total;
34+
}
35+
}

src/repositoies/sig-member/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export default class SigMemberRepository extends Repository<SigMember> {
1212
/**
1313
* List sig members.
1414
* @param sigId SIG id.
15-
* @private
1615
*/
1716
public async listSigMembers(
1817
sigId: number

src/services/contributor/index.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Service } from "typedi";
2+
import { StatusCodes } from "http-status-codes";
3+
import { InjectRepository } from "typeorm-typedi-extensions";
4+
5+
import { Response } from "../response";
6+
import { ContributorsDTO } from "../dtos/ContributorsDTO";
7+
import { PaginateQuery } from "../../queries/PaginateQuery";
8+
import ContributorRepository from "../../repositoies/contributor";
9+
import { ContributorMessage } from "../messages/ContributorMessage";
10+
11+
export interface IContributorService {
12+
/**
13+
* List contributors.
14+
* @param paginateQuery
15+
*/
16+
listContributors(
17+
paginateQuery?: PaginateQuery
18+
): Promise<Response<ContributorsDTO>>;
19+
}
20+
21+
@Service()
22+
export default class ContributorService implements IContributorService {
23+
constructor(
24+
@InjectRepository()
25+
private contributorRepository: ContributorRepository
26+
) {}
27+
28+
/**
29+
* List contributors.
30+
* @param paginateQuery
31+
*/
32+
public async listContributors(
33+
paginateQuery?: PaginateQuery
34+
): Promise<Response<ContributorsDTO>> {
35+
if (paginateQuery === undefined) {
36+
const contributors = (
37+
await this.contributorRepository.listContributors()
38+
).map((c) => {
39+
return { githubName: c };
40+
});
41+
42+
const total = await this.contributorRepository.getContributorsCount();
43+
44+
return {
45+
data: { contributors: contributors, total: total },
46+
status: StatusCodes.OK,
47+
message: ContributorMessage.ListContributorSuccess,
48+
};
49+
} else {
50+
const { current, pageSize } = paginateQuery;
51+
const skip = (current - 1) * pageSize;
52+
53+
const contributors = (
54+
await this.contributorRepository.listContributors(skip, pageSize)
55+
).map((c) => {
56+
return { githubName: c };
57+
});
58+
59+
const total = await this.contributorRepository.getContributorsCount();
60+
61+
return {
62+
data: { contributors: contributors, total: total },
63+
status: StatusCodes.OK,
64+
message: ContributorMessage.ListContributorSuccess,
65+
};
66+
}
67+
}
68+
}

src/services/dtos/ContributorsDTO.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface ContributorsDTO {
2+
contributors: ContributorDTO[];
3+
total: number;
4+
}
5+
6+
interface ContributorDTO {
7+
githubName: string;
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum ContributorMessage {
2+
ListContributorSuccess = "List contributors success.",
3+
IllegalQueryParameters = "Illegal query parameters.",
4+
}

0 commit comments

Comments
 (0)