Skip to content

Commit

Permalink
feat: switching to a generic get one page fn (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpb06 authored May 13, 2024
1 parent 7c2b54b commit 244d96e
Show file tree
Hide file tree
Showing 21 changed files with 100 additions and 214 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const [profile, repos, orgs, events] = await Effect.runPromise(
// Get user events
OctokitLayer.user(username).events(),
],
// Fetch all these in parallel
{ concurrency: 'unbounded' },
),
Effect.provide(OctokitLayerLive),
Expand All @@ -66,7 +67,7 @@ const [profile, repos, orgs, events] = await Effect.runPromise(
```typescript
import { OctokitLayer, OctokitLayerLive } from 'effect-github-stats';

const [profile, repos, orgs, events] = await Effect.runPromise(
const orgs = await Effect.runPromise(
pipe(
// Get organization repos
OctokitLayer.org('my-org').repos();
Expand Down Expand Up @@ -101,6 +102,7 @@ const [issues, pulls, issue34, pull5453, pull5453Reviews] =
// Get pull request #5453 reviews
OctokitLayer.repo(reactRepo).pull(5453).reviews(),
],
// Fetch all these in parallel
{ concurrency: 'unbounded' },
),
Effect.provide(OctokitLayerLive),
Expand All @@ -121,4 +123,4 @@ You can specify the `concurrency` parameter on calls doing several requests in p
github.repo(repo).pulls(100);
```

Note that github api enforces [api rate limits](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#dealing-with-secondary-rate-limits). Getting too many results concurrently will cause an api rate limit. In that case, a warning will be displayed and the call will be attempted again after the time window provided by github api (typically 60 seconds).
Note that github api enforces [api rate limits](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#dealing-with-secondary-rate-limits). Fetching too many results concurrently will cause an api rate limit. In that case, a warning will be displayed and the call will be attempted again after the time window provided by github api (typically 60 seconds).
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"@stylistic/eslint-plugin": "^2.1.0",
"@types/eslint": "^8.56.10",
"@types/node": "^20.12.11",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vitest/coverage-v8": "^1.6.0",
"copyfiles": "^2.4.1",
"del-cli": "^5.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const getPage = (args: GetPullRequestReviewsArgs) => (page: number) =>
});

export const getPullRequestReviews = (args: GetPullRequestReviewsArgs) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-pull-request-reviews', {
attributes: { ...args },
})(getAllPages(getPage, args));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const getPage = (args: GetRepoIssuesArgs) => (page: number) =>
});

export const getRepoIssues = (args: GetRepoIssuesArgs) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-repo-issues', {
attributes: { ...args },
})(getAllPages(getPage, args));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const getPage = (args: GetRepoPullRequestsArgs) => (page: number) =>
});

export const getRepoPullRequests = (args: GetRepoPullRequestsArgs) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-repo-pull-requests', {
attributes: { ...args },
})(getAllPages(getPage, args));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const getPage =
.exhaustive();

export const getRepositories = (args: GetRepositoriesArgs) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-repositories', {
attributes: { ...args },
})(getAllPages(getPage, args));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const getPage = (args: GetUserEventsArgs) => (page: number) =>
});

export const getUserEvents = (args: GetUserEventsArgs) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-user-events', {
attributes: { ...args },
})(getAllPages(getPage, args));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ export const getAllPages = <
getPage: GetPage<TArgs, TData, TError>,
args: TArgs,
) =>
Effect.withSpan(__filename, {
Effect.withSpan('get-all-pages', {
attributes: { ...args },
})(
Effect.gen(function* (_) {
const firstPage = yield* _(getPage(args)(1));

if (firstPage.links?.last === undefined) {
return firstPage.data;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { type Endpoints } from '@octokit/types';
import { RequestParameters } from '@octokit/types/dist-types/RequestParameters';
import { Effect, pipe } from 'effect';

import { handleOctokitRequestError } from '../../../../errors/handle-octokit-request-error';
import { parseLink } from '../../../../logic/parse-link.logic';
import { githubSourceAnalysisProvider } from '../../../../providers/github-source-analysis.provider';
import { retryAfterSchedule } from '../../../../schedules/retry-after.schedule';

import { octokitRequest } from './logic/octokit-request.logic';

export const getOnePage = <E extends keyof Endpoints>(
span: string,
route: E,
options?: Endpoints[E]['parameters'] & RequestParameters,
) =>
Effect.withSpan(span, {
attributes: {
...options,
},
})(
pipe(
githubSourceAnalysisProvider,
Effect.flatMap((octokit) =>
pipe(
Effect.tryPromise({
try: () => octokitRequest(octokit)<E>(route, options),
catch: handleOctokitRequestError,
}),
Effect.retry(retryAfterSchedule),
),
),
Effect.map((response) => ({
data: response.data as Endpoints[E]['response']['data'],
links: parseLink(response),
})),
),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Octokit } from '@octokit/core';
import { type Endpoints } from '@octokit/types';
import { RequestParameters } from '@octokit/types/dist-types/RequestParameters';

export const octokitRequest =
(octokit: Octokit) =>
<E extends keyof Endpoints>(
route: E,
options?: Endpoints[E]['parameters'] & RequestParameters,
): Promise<Endpoints[E]['response']> =>
octokit.request<E>(route, options as never);
39 changes: 6 additions & 33 deletions src/layer/github/implementation/paging/get-org-repos-page.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,16 @@
import { Effect, pipe } from 'effect';

import { EffectResultSuccess } from '../../../../types/effect.types';
import { handleOctokitRequestError } from '../../../errors/handle-octokit-request-error';
import { parseLink } from '../../../logic/parse-link.logic';
import { githubSourceAnalysisProvider } from '../../../providers/github-source-analysis.provider';
import { retryAfterSchedule } from '../../../schedules/retry-after.schedule';
import { getOnePage } from '../generic/get-one-page/get-one-page.effect';

export interface GetOrgReposPageArgs {
org: string;
page: number;
}

export const getOrgReposPage = (args: GetOrgReposPageArgs) =>
Effect.withSpan(__filename, {
attributes: {
...args,
},
})(
pipe(
githubSourceAnalysisProvider,
Effect.flatMap((octokit) =>
pipe(
Effect.tryPromise({
try: () =>
octokit.request('GET /orgs/{org}/repos', {
...args,
type: 'all',
per_page: 100,
}),
catch: handleOctokitRequestError,
}),
Effect.retry(retryAfterSchedule),
),
),
Effect.map((response) => ({
data: response.data,
links: parseLink(response),
})),
),
);
getOnePage('get-org-repos-page', 'GET /orgs/{org}/repos', {
...args,
type: 'all',
per_page: 100,
});

export type OrgReposPageItems = EffectResultSuccess<typeof getOrgReposPage>;
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Effect, pipe } from 'effect';

import { EffectResultSuccess } from '../../../../types/effect.types';
import { handleOctokitRequestError } from '../../../errors/handle-octokit-request-error';
import { parseLink } from '../../../logic/parse-link.logic';
import { githubSourceAnalysisProvider } from '../../../providers/github-source-analysis.provider';
import { retryAfterSchedule } from '../../../schedules/retry-after.schedule';
import { getOnePage } from '../generic/get-one-page/get-one-page.effect';

export interface GetPullRequestReviewsPageArgs {
owner: string;
Expand All @@ -19,40 +14,16 @@ export const getPullRequestReviewsPage = ({
pullNumber,
page,
}: GetPullRequestReviewsPageArgs) =>
Effect.withSpan(__filename, {
attributes: {
getOnePage(
'get-pull-request-reviews-page',
'GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews',
{
owner,
repo,
pullNumber,
pull_number: pullNumber,
per_page: 100,
page,
},
})(
pipe(
githubSourceAnalysisProvider,
Effect.flatMap((octokit) =>
pipe(
Effect.tryPromise({
try: () =>
octokit.request(
'GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews',
{
owner,
repo,
pull_number: pullNumber,
per_page: 100,
page,
},
),
catch: handleOctokitRequestError,
}),
Effect.retry(retryAfterSchedule),
),
),
Effect.map((response) => ({
data: response.data,
links: parseLink(response),
})),
),
);

export type PullRequestReviewsPageItems = EffectResultSuccess<
Expand Down
37 changes: 5 additions & 32 deletions src/layer/github/implementation/paging/get-repo-issues-page.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Effect, pipe } from 'effect';

import { EffectResultSuccess } from '../../../..';
import { handleOctokitRequestError } from '../../../errors/handle-octokit-request-error';
import { parseLink } from '../../../logic/parse-link.logic';
import { githubSourceAnalysisProvider } from '../../../providers/github-source-analysis.provider';
import { retryAfterSchedule } from '../../../schedules/retry-after.schedule';
import { getOnePage } from '../generic/get-one-page/get-one-page.effect';

export interface GetRepoIssuesPageArgs {
owner: string;
Expand All @@ -13,31 +8,9 @@ export interface GetRepoIssuesPageArgs {
}

export const getRepoIssuesPage = (args: GetRepoIssuesPageArgs) =>
Effect.withSpan(__filename, {
attributes: {
...args,
},
})(
pipe(
githubSourceAnalysisProvider,
Effect.flatMap((octokit) =>
pipe(
Effect.tryPromise({
try: () =>
octokit.request('GET /repos/{owner}/{repo}/issues', {
...args,
per_page: 100,
}),
catch: handleOctokitRequestError,
}),
Effect.retry(retryAfterSchedule),
),
),
Effect.map((response) => ({
data: response.data,
links: parseLink(response),
})),
),
);
getOnePage('get-repo-issues-page', 'GET /repos/{owner}/{repo}/issues', {
...args,
per_page: 100,
});

export type IssuesPageItems = EffectResultSuccess<typeof getRepoIssuesPage>;
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { pipe, Effect } from 'effect';

import { EffectResultSuccess } from '../../../../types/effect.types';
import { handleOctokitRequestError } from '../../../errors/handle-octokit-request-error';
import { parseLink } from '../../../logic/parse-link.logic';
import { githubSourceAnalysisProvider } from '../../../providers/github-source-analysis.provider';
import { retryAfterSchedule } from '../../../schedules/retry-after.schedule';
import { getOnePage } from '../generic/get-one-page/get-one-page.effect';

export interface GetRepoPullRequestsPageArgs {
owner: string;
Expand All @@ -13,33 +8,11 @@ export interface GetRepoPullRequestsPageArgs {
}

export const getRepoPullRequestsPage = (args: GetRepoPullRequestsPageArgs) =>
Effect.withSpan(__filename, {
attributes: {
...args,
},
})(
pipe(
githubSourceAnalysisProvider,
Effect.flatMap((octokit) =>
pipe(
Effect.tryPromise({
try: () =>
octokit.request('GET /repos/{owner}/{repo}/pulls', {
...args,
state: 'all',
per_page: 100,
}),
catch: handleOctokitRequestError,
}),
Effect.retry(retryAfterSchedule),
),
),
Effect.map((response) => ({
data: response.data,
links: parseLink(response),
})),
),
);
getOnePage('get-repo-pull-requests-page', 'GET /repos/{owner}/{repo}/pulls', {
...args,
state: 'all',
per_page: 100,
});

export type RepoPullRequestsPageItems = EffectResultSuccess<
typeof getRepoPullRequestsPage
Expand Down
Loading

0 comments on commit 244d96e

Please sign in to comment.