Skip to content

Commit

Permalink
feat: set common error types from Octokit request errors (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
gr2m committed Aug 5, 2020
1 parent 89dca08 commit c57780f
Show file tree
Hide file tree
Showing 10 changed files with 295 additions and 235 deletions.
191 changes: 76 additions & 115 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"author": "Gregor Martynus (https://twitter.com/gr2m)",
"license": "MIT",
"dependencies": {
"@octokit/request-error": "^2.0.2",
"@pika/plugin-ts-standard-pkg": "^0.9.2",
"aggregate-error": "^3.0.1",
"debug": "^4.0.0"
},
"devDependencies": {
Expand Down Expand Up @@ -44,7 +46,7 @@
"pretest": "npm run -s lint && npm run build -s",
"test": "tap --100 --no-coverage 'test/**/*-test.js'",
"generate-types": "node scripts/generate-types.js",
"validate:ts": "tsc --noEmit --noImplicitAny --target es2020 test/typescript-validate.ts"
"validate:ts": "tsc --noEmit --noImplicitAny --target es2020 --esModuleInterop --moduleResolution node test/typescript-validate.ts"
},
"repository": {
"type": "git",
Expand Down
10 changes: 5 additions & 5 deletions scripts/generate-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ const generateEventNameType = (event, name, actions) => `type ${event} =
eventTypes.push(`type ErrorEvent = "error"`, `type WildcardEvent = "*"`);

const conditionalType = [
`export type GetWebhookPayloadTypeFromEvent<T> = `,
`T extends ${eventNamesVariable}.ErrorEvent ? Error :`,
`T extends ${eventNamesVariable}.WildcardEvent ? any :`,
`export type GetWebhookPayloadTypeFromEvent<E = EventNames.All, T = WebhookEvent> = `,
`E extends ${eventNamesVariable}.ErrorEvent ? WebhookEventHandlerError :`,
`E extends ${eventNamesVariable}.WildcardEvent ? any :`,
];

webhooks.forEach(({ name, actions, examples }) => {
Expand All @@ -55,7 +55,7 @@ webhooks.forEach(({ name, actions, examples }) => {

eventTypes.push(eventNameType);
conditionalType.push(
`T extends ${eventNamesVariable}.${eventNameTypeKey} ? WebhookEvent<${eventPayloadsVariable}.${typeName}> :`
`E extends ${eventNamesVariable}.${eventNameTypeKey} ? WebhookEvent<${eventPayloadsVariable}.${typeName}> & T:`
);
});

Expand All @@ -64,7 +64,7 @@ conditionalType.push("never");
const getWebhookPayloadTypeFromEvent = `
import { ${eventNamesVariable} } from "./event-names";
import { ${eventPayloadsVariable} } from "./event-payloads";
import { WebhookEvent } from "../types";
import { WebhookEvent, WebhookEventHandlerError } from "../types";
${conditionalType.join("\n")}
`;
Expand Down
22 changes: 16 additions & 6 deletions src/event-handler/receive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import AggregateError from "aggregate-error";
import { Deprecation } from "deprecation";

import { wrapErrorHandler } from "./wrap-error-handler";
import { WebhookEvent, State } from "../types";
import {
WebhookEvent,
State,
OctokitError,
WebhookEventHandlerError,
} from "../types";
import { EventNames } from "../generated/event-names";

function getHooks(
Expand Down Expand Up @@ -48,7 +56,7 @@ export function receiverHandle(state: State, event: WebhookEvent) {
return Promise.resolve();
}

const errors: Error[] = [];
const errors: OctokitError[] = [];
const promises = hooks.map((handler: Function) => {
let promise = Promise.resolve(event);

Expand All @@ -69,11 +77,13 @@ export function receiverHandle(state: State, event: WebhookEvent) {
return;
}

errorHandlers.forEach((handler) =>
errors.forEach(wrapErrorHandler.bind(null, handler))
);
const error = new AggregateError(errors) as WebhookEventHandlerError;
Object.assign(error, {
event,
errors,
});

const error = Object.assign(new Error("Webhook handler error"), { errors });
errorHandlers.forEach((handler) => wrapErrorHandler(handler, error));

throw error;
});
Expand Down
203 changes: 103 additions & 100 deletions src/generated/get-webhook-payload-type-from-event.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,108 @@
import { EventNames } from "./event-names";
import { EventPayloads } from "./event-payloads";
import { WebhookEvent } from "../types";
import { WebhookEvent, WebhookEventHandlerError } from "../types";

export type GetWebhookPayloadTypeFromEvent<T> = T extends EventNames.ErrorEvent
? Error
: T extends EventNames.WildcardEvent
export type GetWebhookPayloadTypeFromEvent<
E = EventNames.All,
T = WebhookEvent
> = E extends EventNames.ErrorEvent
? WebhookEventHandlerError
: E extends EventNames.WildcardEvent
? any
: T extends EventNames.CheckRunEvent
? WebhookEvent<EventPayloads.WebhookPayloadCheckRun>
: T extends EventNames.CheckSuiteEvent
? WebhookEvent<EventPayloads.WebhookPayloadCheckSuite>
: T extends EventNames.CommitCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadCommitComment>
: T extends EventNames.ContentReferenceEvent
? WebhookEvent<EventPayloads.WebhookPayloadContentReference>
: T extends EventNames.CreateEvent
? WebhookEvent<EventPayloads.WebhookPayloadCreate>
: T extends EventNames.DeleteEvent
? WebhookEvent<EventPayloads.WebhookPayloadDelete>
: T extends EventNames.DeployKeyEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeployKey>
: T extends EventNames.DeploymentEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeployment>
: T extends EventNames.DeploymentStatusEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeploymentStatus>
: T extends EventNames.ForkEvent
? WebhookEvent<EventPayloads.WebhookPayloadFork>
: T extends EventNames.GithubAppAuthorizationEvent
? WebhookEvent<EventPayloads.WebhookPayloadGithubAppAuthorization>
: T extends EventNames.GollumEvent
? WebhookEvent<EventPayloads.WebhookPayloadGollum>
: T extends EventNames.InstallationEvent
? WebhookEvent<EventPayloads.WebhookPayloadInstallation>
: T extends EventNames.InstallationRepositoriesEvent
? WebhookEvent<EventPayloads.WebhookPayloadInstallationRepositories>
: T extends EventNames.IssueCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadIssueComment>
: T extends EventNames.IssuesEvent
? WebhookEvent<EventPayloads.WebhookPayloadIssues>
: T extends EventNames.LabelEvent
? WebhookEvent<EventPayloads.WebhookPayloadLabel>
: T extends EventNames.MarketplacePurchaseEvent
? WebhookEvent<EventPayloads.WebhookPayloadMarketplacePurchase>
: T extends EventNames.MemberEvent
? WebhookEvent<EventPayloads.WebhookPayloadMember>
: T extends EventNames.MembershipEvent
? WebhookEvent<EventPayloads.WebhookPayloadMembership>
: T extends EventNames.MetaEvent
? WebhookEvent<EventPayloads.WebhookPayloadMeta>
: T extends EventNames.MilestoneEvent
? WebhookEvent<EventPayloads.WebhookPayloadMilestone>
: T extends EventNames.OrganizationEvent
? WebhookEvent<EventPayloads.WebhookPayloadOrganization>
: T extends EventNames.OrgBlockEvent
? WebhookEvent<EventPayloads.WebhookPayloadOrgBlock>
: T extends EventNames.PackageEvent
? WebhookEvent<EventPayloads.WebhookPayloadPackage>
: T extends EventNames.PageBuildEvent
? WebhookEvent<EventPayloads.WebhookPayloadPageBuild>
: T extends EventNames.PingEvent
? WebhookEvent<EventPayloads.WebhookPayloadPing>
: T extends EventNames.ProjectCardEvent
? WebhookEvent<EventPayloads.WebhookPayloadProjectCard>
: T extends EventNames.ProjectColumnEvent
? WebhookEvent<EventPayloads.WebhookPayloadProjectColumn>
: T extends EventNames.ProjectEvent
? WebhookEvent<EventPayloads.WebhookPayloadProject>
: T extends EventNames.PublicEvent
? WebhookEvent<EventPayloads.WebhookPayloadPublic>
: T extends EventNames.PullRequestEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequest>
: T extends EventNames.PullRequestReviewEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequestReview>
: T extends EventNames.PullRequestReviewCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequestReviewComment>
: T extends EventNames.PushEvent
? WebhookEvent<EventPayloads.WebhookPayloadPush>
: T extends EventNames.ReleaseEvent
? WebhookEvent<EventPayloads.WebhookPayloadRelease>
: T extends EventNames.RepositoryDispatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryDispatch>
: T extends EventNames.RepositoryEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepository>
: T extends EventNames.RepositoryImportEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryImport>
: T extends EventNames.RepositoryVulnerabilityAlertEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryVulnerabilityAlert>
: T extends EventNames.SecurityAdvisoryEvent
? WebhookEvent<EventPayloads.WebhookPayloadSecurityAdvisory>
: T extends EventNames.SponsorshipEvent
? WebhookEvent<EventPayloads.WebhookPayloadSponsorship>
: T extends EventNames.StarEvent
? WebhookEvent<EventPayloads.WebhookPayloadStar>
: T extends EventNames.StatusEvent
? WebhookEvent<EventPayloads.WebhookPayloadStatus>
: T extends EventNames.TeamEvent
? WebhookEvent<EventPayloads.WebhookPayloadTeam>
: T extends EventNames.TeamAddEvent
? WebhookEvent<EventPayloads.WebhookPayloadTeamAdd>
: T extends EventNames.WatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadWatch>
: T extends EventNames.WorkflowDispatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadWorkflowDispatch>
: E extends EventNames.CheckRunEvent
? WebhookEvent<EventPayloads.WebhookPayloadCheckRun> & T
: E extends EventNames.CheckSuiteEvent
? WebhookEvent<EventPayloads.WebhookPayloadCheckSuite> & T
: E extends EventNames.CommitCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadCommitComment> & T
: E extends EventNames.ContentReferenceEvent
? WebhookEvent<EventPayloads.WebhookPayloadContentReference> & T
: E extends EventNames.CreateEvent
? WebhookEvent<EventPayloads.WebhookPayloadCreate> & T
: E extends EventNames.DeleteEvent
? WebhookEvent<EventPayloads.WebhookPayloadDelete> & T
: E extends EventNames.DeployKeyEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeployKey> & T
: E extends EventNames.DeploymentEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeployment> & T
: E extends EventNames.DeploymentStatusEvent
? WebhookEvent<EventPayloads.WebhookPayloadDeploymentStatus> & T
: E extends EventNames.ForkEvent
? WebhookEvent<EventPayloads.WebhookPayloadFork> & T
: E extends EventNames.GithubAppAuthorizationEvent
? WebhookEvent<EventPayloads.WebhookPayloadGithubAppAuthorization> & T
: E extends EventNames.GollumEvent
? WebhookEvent<EventPayloads.WebhookPayloadGollum> & T
: E extends EventNames.InstallationEvent
? WebhookEvent<EventPayloads.WebhookPayloadInstallation> & T
: E extends EventNames.InstallationRepositoriesEvent
? WebhookEvent<EventPayloads.WebhookPayloadInstallationRepositories> & T
: E extends EventNames.IssueCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadIssueComment> & T
: E extends EventNames.IssuesEvent
? WebhookEvent<EventPayloads.WebhookPayloadIssues> & T
: E extends EventNames.LabelEvent
? WebhookEvent<EventPayloads.WebhookPayloadLabel> & T
: E extends EventNames.MarketplacePurchaseEvent
? WebhookEvent<EventPayloads.WebhookPayloadMarketplacePurchase> & T
: E extends EventNames.MemberEvent
? WebhookEvent<EventPayloads.WebhookPayloadMember> & T
: E extends EventNames.MembershipEvent
? WebhookEvent<EventPayloads.WebhookPayloadMembership> & T
: E extends EventNames.MetaEvent
? WebhookEvent<EventPayloads.WebhookPayloadMeta> & T
: E extends EventNames.MilestoneEvent
? WebhookEvent<EventPayloads.WebhookPayloadMilestone> & T
: E extends EventNames.OrganizationEvent
? WebhookEvent<EventPayloads.WebhookPayloadOrganization> & T
: E extends EventNames.OrgBlockEvent
? WebhookEvent<EventPayloads.WebhookPayloadOrgBlock> & T
: E extends EventNames.PackageEvent
? WebhookEvent<EventPayloads.WebhookPayloadPackage> & T
: E extends EventNames.PageBuildEvent
? WebhookEvent<EventPayloads.WebhookPayloadPageBuild> & T
: E extends EventNames.PingEvent
? WebhookEvent<EventPayloads.WebhookPayloadPing> & T
: E extends EventNames.ProjectCardEvent
? WebhookEvent<EventPayloads.WebhookPayloadProjectCard> & T
: E extends EventNames.ProjectColumnEvent
? WebhookEvent<EventPayloads.WebhookPayloadProjectColumn> & T
: E extends EventNames.ProjectEvent
? WebhookEvent<EventPayloads.WebhookPayloadProject> & T
: E extends EventNames.PublicEvent
? WebhookEvent<EventPayloads.WebhookPayloadPublic> & T
: E extends EventNames.PullRequestEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequest> & T
: E extends EventNames.PullRequestReviewEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequestReview> & T
: E extends EventNames.PullRequestReviewCommentEvent
? WebhookEvent<EventPayloads.WebhookPayloadPullRequestReviewComment> & T
: E extends EventNames.PushEvent
? WebhookEvent<EventPayloads.WebhookPayloadPush> & T
: E extends EventNames.ReleaseEvent
? WebhookEvent<EventPayloads.WebhookPayloadRelease> & T
: E extends EventNames.RepositoryDispatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryDispatch> & T
: E extends EventNames.RepositoryEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepository> & T
: E extends EventNames.RepositoryImportEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryImport> & T
: E extends EventNames.RepositoryVulnerabilityAlertEvent
? WebhookEvent<EventPayloads.WebhookPayloadRepositoryVulnerabilityAlert> & T
: E extends EventNames.SecurityAdvisoryEvent
? WebhookEvent<EventPayloads.WebhookPayloadSecurityAdvisory> & T
: E extends EventNames.SponsorshipEvent
? WebhookEvent<EventPayloads.WebhookPayloadSponsorship> & T
: E extends EventNames.StarEvent
? WebhookEvent<EventPayloads.WebhookPayloadStar> & T
: E extends EventNames.StatusEvent
? WebhookEvent<EventPayloads.WebhookPayloadStatus> & T
: E extends EventNames.TeamEvent
? WebhookEvent<EventPayloads.WebhookPayloadTeam> & T
: E extends EventNames.TeamAddEvent
? WebhookEvent<EventPayloads.WebhookPayloadTeamAdd> & T
: E extends EventNames.WatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadWatch> & T
: E extends EventNames.WorkflowDispatchEvent
? WebhookEvent<EventPayloads.WebhookPayloadWorkflowDispatch> & T
: never;
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ class Webhooks<T extends WebhookEvent = WebhookEvent> {
public on: <E extends EventNames.All>(
event: E | E[],
callback: (
event: GetWebhookPayloadTypeFromEvent<E> & T
event: GetWebhookPayloadTypeFromEvent<E, T>
) => Promise<void> | void
) => void;
public removeListener: <E extends EventNames.All>(
event: E | E[],
callback: (event: GetWebhookPayloadTypeFromEvent<E>) => Promise<void> | void
callback: (
event: GetWebhookPayloadTypeFromEvent<E, T>
) => Promise<void> | void
) => void;
public receive: (options: {
id: string;
Expand Down
37 changes: 37 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { RequestError } from "@octokit/request-error";

import { EventNames } from "./generated/event-names";
export interface WebhookEvent<T = any> {
id: string;
Expand All @@ -23,3 +25,38 @@ export interface State extends Options<any> {
eventHandler?: any;
hooks: Hooks;
}

/**
* Error object with optional poperties coming from `octokit.request` errors
*/
export type OctokitError = Error &
Partial<RequestError> & {
/**
* @deprecated `error.event` is deprecated. Use the `.event` property on the aggregated error instance
*/
event: WebhookEvent;
};

export interface WebhookEventHandlerError extends AggregateError<OctokitError> {
event: WebhookEvent;

/**
* @deprecated `error.errors` is deprecated. Use `Array.from(error)`. See https://npm.im/aggregate-error
*/
errors: OctokitError[];
}

// temporary using a custom AggregateError type.
// Replace with `import AggregateError from "aggregate-error"` once
// https://github.com/gr2m/aggregate-error/pull/1 is merged or resolved

/**
Create an error from multiple errors.
*/
declare class AggregateError<T extends Error = Error> extends Error
implements Iterable<T> {
readonly name: "AggregateError";
constructor(errors: ReadonlyArray<T | { [key: string]: any } | string>);

[Symbol.iterator](): IterableIterator<T>;
}
Loading

0 comments on commit c57780f

Please sign in to comment.