Skip to content
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
42 changes: 40 additions & 2 deletions packages/das/src/api/miners/miners.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,31 @@ export class MinersController {
})
@ApiParam({ name: "githubId", description: "GitHub user ID (numeric)" })
@ApiBody(SINCE_BY_REPO_API_BODY)
@ApiQuery({
name: "cursor",
required: false,
description:
"Opaque pagination cursor from a previous response's next_cursor field.",
})
@ApiQuery({
name: "limit",
required: false,
description: "Page size (default 50, max 200).",
})
async postPullRequests(
@Param("githubId") githubId: string,
@Body() body: SinceByRepoBody,
@Query("cursor") cursor?: string,
@Query("limit") limit?: string,
): Promise<unknown> {
const { repoNames, sinceValues } = parseSinceByRepo(body);
return this.miners.getPullRequestsByRepo(githubId, repoNames, sinceValues);
const pagination = parsePaginationQuery(limit, cursor);
return this.miners.getPullRequestsByRepo(
githubId,
repoNames,
sinceValues,
pagination,
);
}

@Get(":githubId/issues")
Expand Down Expand Up @@ -216,11 +235,30 @@ export class MinersController {
})
@ApiParam({ name: "githubId", description: "GitHub user ID (numeric)" })
@ApiBody(SINCE_BY_REPO_API_BODY)
@ApiQuery({
name: "cursor",
required: false,
description:
"Opaque pagination cursor from a previous response's next_cursor field.",
})
@ApiQuery({
name: "limit",
required: false,
description: "Page size (default 50, max 200).",
})
async postIssues(
@Param("githubId") githubId: string,
@Body() body: SinceByRepoBody,
@Query("cursor") cursor?: string,
@Query("limit") limit?: string,
): Promise<unknown> {
const { repoNames, sinceValues } = parseSinceByRepo(body);
return this.miners.getIssuesByRepo(githubId, repoNames, sinceValues);
const pagination = parsePaginationQuery(limit, cursor);
return this.miners.getIssuesByRepo(
githubId,
repoNames,
sinceValues,
pagination,
);
}
}
98 changes: 92 additions & 6 deletions packages/das/src/api/miners/miners.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,33 @@ export class MinersService {
githubId: string,
repoNames: string[],
sinceValues: string[],
pagination: PaginationParams | null,
): Promise<{
github_id: string;
since: null;
generated_at: string;
pull_requests: unknown[];
next_cursor?: string | null;
}> {
const cursor = pagination?.cursor ?? null;
const keysetClause = cursor
? `AND (p.created_at, LOWER(p.repo_full_name), p.pr_number) < ($4::timestamptz, $5, $6::int)`
: "";
const limitClause = pagination ? `LIMIT $${cursor ? 7 : 4}` : "";
const params: unknown[] = !pagination
? [githubId, repoNames, sinceValues]
: cursor
? [
githubId,
repoNames,
sinceValues,
cursor.createdAt,
cursor.repoFullName,
cursor.number,
pagination.limit + 1,
]
: [githubId, repoNames, sinceValues, pagination.limit + 1];

const rows = await this.dataSource.query(
`
WITH windows AS (
Expand All @@ -287,16 +308,38 @@ export class MinersService {
OR (p.state = 'MERGED' AND p.merged_at >= w.since)
OR (p.state = 'CLOSED' AND p.created_at >= w.since)
)
ORDER BY p.created_at DESC
${keysetClause}
ORDER BY p.created_at DESC, LOWER(p.repo_full_name) DESC, p.pr_number DESC
${limitClause}
`,
[githubId, repoNames, sinceValues],
params,
);

if (!pagination) {
return {
github_id: githubId,
since: null,
generated_at: new Date().toISOString(),
pull_requests: rows,
};
}

const page = buildPaginatedResponse(
rows as Record<string, unknown>[],
pagination.limit,
(row) => ({
created_at: row.created_at as string,
repo_full_name: row.repo_full_name as string,
pr_number: row.pr_number as number,
}),
);

return {
github_id: githubId,
since: null,
generated_at: new Date().toISOString(),
pull_requests: rows,
pull_requests: page.items,
next_cursor: page.nextCursor,
};
}

Expand Down Expand Up @@ -383,12 +426,33 @@ export class MinersService {
githubId: string,
repoNames: string[],
sinceValues: string[],
pagination: PaginationParams | null,
): Promise<{
github_id: string;
since: null;
generated_at: string;
issues: unknown[];
next_cursor?: string | null;
}> {
const cursor = pagination?.cursor ?? null;
const keysetClause = cursor
? `AND (i.created_at, LOWER(i.repo_full_name), i.issue_number) < ($4::timestamptz, $5, $6::int)`
: "";
const limitClause = pagination ? `LIMIT $${cursor ? 7 : 4}` : "";
const params: unknown[] = !pagination
? [githubId, repoNames, sinceValues]
: cursor
? [
githubId,
repoNames,
sinceValues,
cursor.createdAt,
cursor.repoFullName,
cursor.number,
pagination.limit + 1,
]
: [githubId, repoNames, sinceValues, pagination.limit + 1];

const rows = await this.dataSource.query(
`
WITH windows AS (
Expand All @@ -403,16 +467,38 @@ export class MinersService {
(i.state = 'OPEN' AND i.created_at >= w.since)
OR (i.state = 'CLOSED' AND i.closed_at >= w.since)
)
ORDER BY i.created_at DESC
${keysetClause}
ORDER BY i.created_at DESC, LOWER(i.repo_full_name) DESC, i.issue_number DESC
${limitClause}
`,
[githubId, repoNames, sinceValues],
params,
);

if (!pagination) {
return {
github_id: githubId,
since: null,
generated_at: new Date().toISOString(),
issues: rows,
};
}

const page = buildPaginatedResponse(
rows as Record<string, unknown>[],
pagination.limit,
(row) => ({
created_at: row.created_at as string,
repo_full_name: row.repo_full_name as string,
issue_number: row.issue_number as number,
}),
);

return {
github_id: githubId,
since: null,
generated_at: new Date().toISOString(),
issues: rows,
issues: page.items,
next_cursor: page.nextCursor,
};
}

Expand Down
Loading