Skip to content

Commit d6ec68a

Browse files
authored
Use ExposableError for public errors in GitHub integration (#624)
* Use ExposableError for public errors in GitHub integration * Add changeset
1 parent 9bce519 commit d6ec68a

File tree

5 files changed

+19
-18
lines changed

5 files changed

+19
-18
lines changed

.changeset/lazy-singers-work.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/integration-github': minor
3+
---
4+
5+
Use ExposableError for public errors in GitHub integration

integrations/github/src/api.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import LinkHeader from 'http-link-header';
2-
import { StatusError } from 'itty-router';
32

4-
import { Logger } from '@gitbook/runtime';
3+
import { Logger, ExposableError } from '@gitbook/runtime';
54

65
import type { GithubRuntimeContext, GitHubSpaceConfiguration } from './types';
76
import { assertIsDefined, getSpaceConfigOrThrow } from './utils';
@@ -343,7 +342,7 @@ async function requestGitHubAPI(
343342
logger.error(`[${options.method}] (${response.status}) GitHub API error: ${text}`);
344343

345344
// Otherwise, we throw an error
346-
throw new StatusError(response.status, `GitHub API error: ${response.statusText}`);
345+
throw new ExposableError(`GitHub API error: ${response.statusText}`, response.status);
347346
}
348347

349348
return response;
@@ -370,7 +369,7 @@ async function refreshCredentials(
370369

371370
if (!resp.ok) {
372371
// If refresh fails for whatever reason, we ask the user to re-authenticate
373-
throw new StatusError(401, `Unauthorized: kindly re-authenticate!`);
372+
throw new ExposableError(`Unauthorized: kindly re-authenticate!`, 401);
374373
}
375374

376375
const data = await resp.formData();
@@ -398,7 +397,7 @@ export function extractTokenCredentialsOrThrow(
398397

399398
const oAuthCredentials = config?.oauth_credentials;
400399
if (!oAuthCredentials?.access_token) {
401-
throw new StatusError(401, 'Unauthorized: kindly re-authenticate!');
400+
throw new ExposableError('Unauthorized: kindly re-authenticate!', 401);
402401
}
403402

404403
return oAuthCredentials;

integrations/github/src/index.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Router, error, StatusError } from 'itty-router';
1+
import { Router, error } from 'itty-router';
22

33
import { ContentKitIcon, ContentKitSelectOption, GitSyncOperationState } from '@gitbook/api';
44
import {
@@ -7,6 +7,7 @@ import {
77
createOAuthHandler,
88
Logger,
99
EventCallback,
10+
ExposableError,
1011
} from '@gitbook/runtime';
1112

1213
import {
@@ -76,7 +77,7 @@ const handleFetchEvent: FetchEventCallback<GithubRuntimeContext> = async (reques
7677
if (!verified) {
7778
const message = `Invalid signature for integration task`;
7879
logger.error(message);
79-
throw new StatusError(400, message);
80+
throw new ExposableError(message);
8081
}
8182

8283
const { task } = JSON.parse(payloadString) as { task: IntegrationTask };
@@ -113,7 +114,7 @@ const handleFetchEvent: FetchEventCallback<GithubRuntimeContext> = async (reques
113114
);
114115
} catch (error: any) {
115116
logger.error(`Error verifying signature ${error}`);
116-
throw new StatusError(400, error.message);
117+
throw new ExposableError(error.message);
117118
}
118119

119120
logger.debug('received webhook event', { id, event });

integrations/github/src/installation.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { StatusError } from 'itty-router';
2-
31
import { IntegrationSpaceInstallation } from '@gitbook/api';
4-
import { Logger } from '@gitbook/runtime';
2+
import { Logger, ExposableError } from '@gitbook/runtime';
53

64
import { fetchRepository } from './api';
75
import { triggerExport, triggerImport } from './sync';
@@ -28,8 +26,7 @@ export async function saveSpaceConfiguration(
2826
assertIsDefined(spaceInstallation, { label: 'spaceInstallation' });
2927

3028
if (!state.installation || !state.repository || !state.branch) {
31-
throw new StatusError(
32-
400,
29+
throw new ExposableError(
3330
'Incomplete configuration: missing installation, repository or branch',
3431
);
3532
}

integrations/github/src/webhooks.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import type {
33
PullRequestOpenedEvent,
44
PullRequestSynchronizeEvent,
55
} from '@octokit/webhooks-types';
6-
import { StatusError } from 'itty-router';
76

8-
import { Logger } from '@gitbook/runtime';
7+
import { Logger, ExposableError } from '@gitbook/runtime';
98

109
import { handleImportDispatchForSpaces } from './tasks';
1110
import { GithubRuntimeContext } from './types';
@@ -24,9 +23,9 @@ export async function verifyGitHubWebhookSignature(
2423
secret: string,
2524
) {
2625
if (!signature) {
27-
throw new StatusError(400, 'No signature found on request');
26+
throw new ExposableError('No signature found on request');
2827
} else if (!signature.startsWith('sha256=')) {
29-
throw new StatusError(400, 'Invalid format: signature is not using sha256');
28+
throw new ExposableError('Invalid format: signature is not using sha256');
3029
}
3130

3231
const algorithm = { name: 'HMAC', hash: 'SHA-256' };
@@ -39,7 +38,7 @@ export async function verifyGitHubWebhookSignature(
3938
const signed = await crypto.subtle.sign(algorithm.name, key, enc.encode(payload));
4039
const expectedSignature = `sha256=${arrayToHex(signed)}`;
4140
if (!safeCompare(expectedSignature, signature)) {
42-
throw new StatusError(400, 'Signature does not match event payload and secret');
41+
throw new ExposableError('Signature does not match event payload and secret');
4342
}
4443

4544
// All good!

0 commit comments

Comments
 (0)