@useairfoil/producer-github 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -2
- package/dist/index.d.mts +1939 -0
- package/dist/index.mjs +3 -0
- package/dist/main.d.mts +1 -0
- package/dist/main.mjs +698 -0
- package/dist/main.mjs.map +1 -0
- package/dist/manifest.d.mts +56 -0
- package/dist/manifest.mjs +137 -0
- package/dist/manifest.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/src-D6cTmVtF.mjs +1566 -0
- package/dist/src-D6cTmVtF.mjs.map +1 -0
- package/package.json +48 -4
- package/src/client/auth.ts +113 -0
- package/src/client/client.ts +192 -0
- package/src/client/constants.ts +4 -0
- package/src/connector.ts +97 -0
- package/src/create-table.ts +5 -0
- package/src/demo/changes.ts +420 -0
- package/src/demo/index.ts +59 -0
- package/src/demo/setup.ts +34 -0
- package/src/demo/shared.ts +101 -0
- package/src/index.ts +13 -0
- package/src/main.ts +22 -0
- package/src/manifest.ts +45 -0
- package/src/resources/check-runs.ts +72 -0
- package/src/resources/commit-statuses.ts +84 -0
- package/src/resources/commits.ts +101 -0
- package/src/resources/issue-comments.ts +75 -0
- package/src/resources/issues.ts +78 -0
- package/src/resources/pull-request-commits.ts +91 -0
- package/src/resources/pull-request-review-comments.ts +78 -0
- package/src/resources/pull-request-reviews.ts +76 -0
- package/src/resources/pull-requests.ts +79 -0
- package/src/resources/releases.ts +74 -0
- package/src/resources/repositories.ts +101 -0
- package/src/resources/shared.ts +480 -0
- package/src/resources/workflow-jobs.ts +73 -0
- package/src/resources/workflow-runs.ts +70 -0
- package/src/sandbox.ts +35 -0
- package/src/schemas/check-runs.ts +48 -0
- package/src/schemas/commit-statuses.ts +31 -0
- package/src/schemas/commits.ts +64 -0
- package/src/schemas/events.ts +139 -0
- package/src/schemas/issue-comments.ts +38 -0
- package/src/schemas/issues.ts +62 -0
- package/src/schemas/pull-request-commits.ts +28 -0
- package/src/schemas/pull-request-review-comments.ts +67 -0
- package/src/schemas/pull-request-reviews.ts +37 -0
- package/src/schemas/pull-requests.ts +75 -0
- package/src/schemas/releases.ts +53 -0
- package/src/schemas/repositories.ts +53 -0
- package/src/schemas/shared.ts +86 -0
- package/src/schemas/workflow-jobs.ts +54 -0
- package/src/schemas/workflow-runs.ts +55 -0
- package/src/start.ts +47 -0
- package/src/tables.ts +31 -0
- package/src/webhook/route.ts +46 -0
- package/src/webhook/signature.ts +20 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ConnectorError, Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
import { DateTime, Effect, Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
import type { GitHubClientService } from "../client/client";
|
|
5
|
+
import type { PullRequestObject } from "../schemas/pull-requests";
|
|
6
|
+
import type { RepositoryRef } from "../schemas/repositories";
|
|
7
|
+
|
|
8
|
+
import { PullRequestReviewEventSchema, WebhookPayloadSchema } from "../schemas/events";
|
|
9
|
+
import {
|
|
10
|
+
type PullRequestReview,
|
|
11
|
+
type PullRequestReviewObject,
|
|
12
|
+
PullRequestReviewObjectSchema,
|
|
13
|
+
PullRequestReviewSchema,
|
|
14
|
+
} from "../schemas/pull-request-reviews";
|
|
15
|
+
import { pullRequestSpec } from "./pull-requests";
|
|
16
|
+
import {
|
|
17
|
+
type RepoResourceSpec,
|
|
18
|
+
backfillRepoResource,
|
|
19
|
+
changesRepoResource,
|
|
20
|
+
checkRepoResource,
|
|
21
|
+
childRows,
|
|
22
|
+
decodeEvent,
|
|
23
|
+
} from "./shared";
|
|
24
|
+
|
|
25
|
+
const toRow = (
|
|
26
|
+
review: PullRequestReviewObject,
|
|
27
|
+
pull_request_number: bigint,
|
|
28
|
+
repository_id: bigint,
|
|
29
|
+
version: Date,
|
|
30
|
+
): PullRequestReview => ({ ...review, version, repository_id, pull_request_number });
|
|
31
|
+
|
|
32
|
+
/** A review bumps its pull request's update time, so changes follow pull requests. */
|
|
33
|
+
const reviewSpec = (
|
|
34
|
+
client: GitHubClientService,
|
|
35
|
+
): RepoResourceSpec<PullRequestObject, PullRequestReview> => ({
|
|
36
|
+
...pullRequestSpec,
|
|
37
|
+
rows: childRows(client, {
|
|
38
|
+
path: (pullRequest) => `pulls/${pullRequest.number}/reviews`,
|
|
39
|
+
pageSchema: Schema.Array(PullRequestReviewObjectSchema),
|
|
40
|
+
toRow: (review, pullRequest, repositoryId, readAt) =>
|
|
41
|
+
toRow(review, pullRequest.number, repositoryId, readAt),
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const makePullRequestReviews = (
|
|
46
|
+
client: GitHubClientService,
|
|
47
|
+
repositories: Effect.Effect<ReadonlyArray<RepositoryRef>, ConnectorError>,
|
|
48
|
+
) => {
|
|
49
|
+
const spec = reviewSpec(client);
|
|
50
|
+
return Resource.entity({
|
|
51
|
+
name: "pull_request_reviews",
|
|
52
|
+
rowSchema: PullRequestReviewSchema,
|
|
53
|
+
version: "version",
|
|
54
|
+
|
|
55
|
+
check: checkRepoResource(client, spec),
|
|
56
|
+
backfill: backfillRepoResource(client, repositories, spec),
|
|
57
|
+
changes: changesRepoResource(client, spec),
|
|
58
|
+
webhook: {
|
|
59
|
+
schema: WebhookPayloadSchema,
|
|
60
|
+
// No update time, so read the latest state instead of trusting the payload.
|
|
61
|
+
handler: ({ payload }) =>
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const { repository, pull_request, review } = yield* decodeEvent(
|
|
64
|
+
PullRequestReviewEventSchema,
|
|
65
|
+
payload,
|
|
66
|
+
);
|
|
67
|
+
const readAt = yield* DateTime.nowAsDate;
|
|
68
|
+
const page = yield* client.get(
|
|
69
|
+
PullRequestReviewObjectSchema,
|
|
70
|
+
`/repos/${repository.full_name}/pulls/${pull_request.number}/reviews/${review.id}`,
|
|
71
|
+
);
|
|
72
|
+
return [toRow(page.body, pull_request.number, repository.id, readAt)];
|
|
73
|
+
}),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type ConnectorError, Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
import type { GitHubClientService } from "../client/client";
|
|
5
|
+
import type { RepositoryRef } from "../schemas/repositories";
|
|
6
|
+
|
|
7
|
+
import { PullRequestEventSchema, WebhookPayloadSchema } from "../schemas/events";
|
|
8
|
+
import {
|
|
9
|
+
type PullRequest,
|
|
10
|
+
type PullRequestObject,
|
|
11
|
+
PullRequestObjectSchema,
|
|
12
|
+
PullRequestSchema,
|
|
13
|
+
} from "../schemas/pull-requests";
|
|
14
|
+
import {
|
|
15
|
+
type RepoResourceSpec,
|
|
16
|
+
backfillRepoResource,
|
|
17
|
+
changesRepoResource,
|
|
18
|
+
checkRepoResource,
|
|
19
|
+
decodeEvent,
|
|
20
|
+
rowsOf,
|
|
21
|
+
timestamps,
|
|
22
|
+
} from "./shared";
|
|
23
|
+
|
|
24
|
+
const toRow = (
|
|
25
|
+
{ updated_at, labels, assignees, requested_reviewers, ...rest }: PullRequestObject,
|
|
26
|
+
repository_id: bigint,
|
|
27
|
+
): PullRequest => ({
|
|
28
|
+
...rest,
|
|
29
|
+
version: updated_at,
|
|
30
|
+
repository_id,
|
|
31
|
+
labels: labels ?? [],
|
|
32
|
+
assignees: assignees ?? [],
|
|
33
|
+
requested_reviewers: requested_reviewers ?? [],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// A webhook can leave a list out, so read the pull request again.
|
|
37
|
+
const hasLists = ({ assignees, requested_reviewers }: PullRequestObject) =>
|
|
38
|
+
assignees !== undefined && requested_reviewers !== undefined;
|
|
39
|
+
|
|
40
|
+
export const pullRequestSpec: RepoResourceSpec<PullRequestObject, PullRequest> = {
|
|
41
|
+
path: "pulls",
|
|
42
|
+
pageSchema: Schema.Array(PullRequestObjectSchema),
|
|
43
|
+
params: { state: "all" },
|
|
44
|
+
// The pulls list has no `since`.
|
|
45
|
+
since: false,
|
|
46
|
+
order: "sorted",
|
|
47
|
+
...timestamps,
|
|
48
|
+
keep: () => true,
|
|
49
|
+
enabled: (repository) => repository.has_pull_requests,
|
|
50
|
+
rows: rowsOf(toRow),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const makePullRequests = (
|
|
54
|
+
client: GitHubClientService,
|
|
55
|
+
repositories: Effect.Effect<ReadonlyArray<RepositoryRef>, ConnectorError>,
|
|
56
|
+
) =>
|
|
57
|
+
Resource.entity({
|
|
58
|
+
name: "pull_requests",
|
|
59
|
+
rowSchema: PullRequestSchema,
|
|
60
|
+
version: "version",
|
|
61
|
+
|
|
62
|
+
check: checkRepoResource(client, pullRequestSpec),
|
|
63
|
+
backfill: backfillRepoResource(client, repositories, pullRequestSpec),
|
|
64
|
+
changes: changesRepoResource(client, pullRequestSpec),
|
|
65
|
+
webhook: {
|
|
66
|
+
schema: WebhookPayloadSchema,
|
|
67
|
+
handler: ({ payload }) =>
|
|
68
|
+
Effect.gen(function* () {
|
|
69
|
+
const { pull_request, repository } = yield* decodeEvent(PullRequestEventSchema, payload);
|
|
70
|
+
const full = hasLists(pull_request)
|
|
71
|
+
? pull_request
|
|
72
|
+
: (yield* client.get(
|
|
73
|
+
PullRequestObjectSchema,
|
|
74
|
+
`/repos/${repository.full_name}/pulls/${pull_request.number}`,
|
|
75
|
+
)).body;
|
|
76
|
+
return [toRow(full, repository.id)];
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ConnectorError, Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Effect, Schema, Struct } from "effect";
|
|
3
|
+
|
|
4
|
+
import type { GitHubClientService } from "../client/client";
|
|
5
|
+
import type { RepositoryRef } from "../schemas/repositories";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
ReleaseDeletedEventSchema,
|
|
9
|
+
ReleaseEventSchema,
|
|
10
|
+
WebhookPayloadSchema,
|
|
11
|
+
} from "../schemas/events";
|
|
12
|
+
import {
|
|
13
|
+
type Release,
|
|
14
|
+
type ReleaseObject,
|
|
15
|
+
ReleaseObjectSchema,
|
|
16
|
+
ReleaseSchema,
|
|
17
|
+
} from "../schemas/releases";
|
|
18
|
+
import {
|
|
19
|
+
type RepoResourceSpec,
|
|
20
|
+
backfillRepoResource,
|
|
21
|
+
changesRepoResource,
|
|
22
|
+
checkRepoResource,
|
|
23
|
+
decodeEvent,
|
|
24
|
+
deleteRow,
|
|
25
|
+
rowsOf,
|
|
26
|
+
} from "./shared";
|
|
27
|
+
|
|
28
|
+
const updatedAt = (release: ReleaseObject) => release.updated_at ?? release.created_at;
|
|
29
|
+
|
|
30
|
+
const toRow = (release: ReleaseObject, repository_id: bigint): Release => ({
|
|
31
|
+
...Struct.omit(release, ["updated_at"]),
|
|
32
|
+
version: updatedAt(release),
|
|
33
|
+
repository_id,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const releaseSpec: RepoResourceSpec<ReleaseObject, Release> = {
|
|
37
|
+
path: "releases",
|
|
38
|
+
pageSchema: Schema.Array(ReleaseObjectSchema),
|
|
39
|
+
params: {},
|
|
40
|
+
since: false,
|
|
41
|
+
// The list can't sort or filter, so changes read it all. Most repositories
|
|
42
|
+
// have few releases.
|
|
43
|
+
order: "newest",
|
|
44
|
+
createdAt: (release) => release.created_at,
|
|
45
|
+
updatedAt,
|
|
46
|
+
keep: () => true,
|
|
47
|
+
enabled: () => true,
|
|
48
|
+
rows: rowsOf(toRow),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const makeReleases = (
|
|
52
|
+
client: GitHubClientService,
|
|
53
|
+
repositories: Effect.Effect<ReadonlyArray<RepositoryRef>, ConnectorError>,
|
|
54
|
+
) =>
|
|
55
|
+
Resource.entity({
|
|
56
|
+
name: "releases",
|
|
57
|
+
rowSchema: ReleaseSchema,
|
|
58
|
+
version: "version",
|
|
59
|
+
|
|
60
|
+
check: checkRepoResource(client, releaseSpec),
|
|
61
|
+
backfill: backfillRepoResource(client, repositories, releaseSpec),
|
|
62
|
+
changes: changesRepoResource(client, releaseSpec),
|
|
63
|
+
webhook: {
|
|
64
|
+
schema: WebhookPayloadSchema,
|
|
65
|
+
handler: ({ payload }) =>
|
|
66
|
+
Effect.gen(function* () {
|
|
67
|
+
const event = yield* decodeEvent(ReleaseEventSchema, payload);
|
|
68
|
+
if (Schema.is(ReleaseDeletedEventSchema)(event)) {
|
|
69
|
+
return [yield* deleteRow(event.release.id)];
|
|
70
|
+
}
|
|
71
|
+
return [toRow(event.release, event.repository.id)];
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Cursor, Fetch, Resource } from "@useairfoil/connector-kit";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
import { type GitHubClientService, withParams } from "../client/client";
|
|
5
|
+
import {
|
|
6
|
+
RepositoryDeletedEventSchema,
|
|
7
|
+
RepositoryEventSchema,
|
|
8
|
+
WebhookPayloadSchema,
|
|
9
|
+
} from "../schemas/events";
|
|
10
|
+
import {
|
|
11
|
+
type Repository,
|
|
12
|
+
type RepositoryObject,
|
|
13
|
+
RepositoryObjectSchema,
|
|
14
|
+
RepositorySchema,
|
|
15
|
+
} from "../schemas/repositories";
|
|
16
|
+
import {
|
|
17
|
+
changesInterval,
|
|
18
|
+
decodeEvent,
|
|
19
|
+
deleteRow,
|
|
20
|
+
installationRepositoriesPath,
|
|
21
|
+
isSince,
|
|
22
|
+
listRepositories,
|
|
23
|
+
pageSize,
|
|
24
|
+
parseCutoff,
|
|
25
|
+
readChanges,
|
|
26
|
+
repositoryPage,
|
|
27
|
+
} from "./shared";
|
|
28
|
+
|
|
29
|
+
const toRow = ({ updated_at, topics, ...rest }: RepositoryObject): Repository => ({
|
|
30
|
+
...rest,
|
|
31
|
+
version: updated_at,
|
|
32
|
+
topics: topics ?? [],
|
|
33
|
+
_deleted: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const makeRepositories = (client: GitHubClientService) =>
|
|
37
|
+
Resource.entity({
|
|
38
|
+
name: "repositories",
|
|
39
|
+
rowSchema: RepositorySchema,
|
|
40
|
+
version: "version",
|
|
41
|
+
|
|
42
|
+
check: client
|
|
43
|
+
.get(
|
|
44
|
+
repositoryPage(RepositoryObjectSchema),
|
|
45
|
+
withParams(installationRepositoriesPath, { per_page: "1" }),
|
|
46
|
+
)
|
|
47
|
+
.pipe(Effect.asVoid),
|
|
48
|
+
// The installation's list can't filter on time, so the cutoff is applied here.
|
|
49
|
+
backfill: Fetch.page({
|
|
50
|
+
pageCursor: Cursor.string(),
|
|
51
|
+
cutoff: Cursor.isoDateTime(),
|
|
52
|
+
fetch: ({ pageCursor, cutoff }) =>
|
|
53
|
+
Effect.gen(function* () {
|
|
54
|
+
const cutoffDate = parseCutoff(String(cutoff));
|
|
55
|
+
const page = yield* client.get(
|
|
56
|
+
repositoryPage(RepositoryObjectSchema),
|
|
57
|
+
typeof pageCursor === "string"
|
|
58
|
+
? pageCursor
|
|
59
|
+
: withParams(installationRepositoriesPath, { per_page: pageSize }),
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
rows: page.body.repositories
|
|
63
|
+
.filter((repository) => repository.created_at.getTime() <= cutoffDate.getTime())
|
|
64
|
+
.map(toRow),
|
|
65
|
+
nextPageCursor: Option.getOrUndefined(page.next),
|
|
66
|
+
hasMore: Option.isSome(page.next),
|
|
67
|
+
};
|
|
68
|
+
}),
|
|
69
|
+
}),
|
|
70
|
+
// New repositories are kept too, so one added later still gets a row.
|
|
71
|
+
changes: Fetch.changes({
|
|
72
|
+
cursor: Cursor.string(),
|
|
73
|
+
interval: changesInterval,
|
|
74
|
+
fetch: ({ cursor }) =>
|
|
75
|
+
readChanges(
|
|
76
|
+
String(cursor),
|
|
77
|
+
listRepositories(client, RepositoryObjectSchema),
|
|
78
|
+
(repositories) =>
|
|
79
|
+
repositories
|
|
80
|
+
.filter(({ repository, since }) => isSince(since, repository.updated_at))
|
|
81
|
+
.map(({ repository }) => toRow(repository)),
|
|
82
|
+
),
|
|
83
|
+
}),
|
|
84
|
+
webhook: {
|
|
85
|
+
schema: WebhookPayloadSchema,
|
|
86
|
+
handler: ({ payload }) =>
|
|
87
|
+
Effect.gen(function* () {
|
|
88
|
+
const event = yield* decodeEvent(RepositoryEventSchema, payload);
|
|
89
|
+
if (Schema.is(RepositoryDeletedEventSchema)(event)) {
|
|
90
|
+
return [yield* deleteRow(event.repository.id)];
|
|
91
|
+
}
|
|
92
|
+
const { repository } = event;
|
|
93
|
+
// A webhook can leave the topics out, so read the repository again.
|
|
94
|
+
const full =
|
|
95
|
+
repository.topics !== undefined
|
|
96
|
+
? repository
|
|
97
|
+
: (yield* client.get(RepositoryObjectSchema, `/repos/${repository.full_name}`)).body;
|
|
98
|
+
return [toRow(full)];
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
});
|