Skip to content

Commit

Permalink
feat: Allow new webhooks to be created with automatic secret generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-coster committed Sep 23, 2021
1 parent 11a7b02 commit 372ae73
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 4 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ await card.attach('path/to/a/file.txt');
Favro signs webhooks with the secret you provide during their creation. The `BravoWebhookDefinition` class has a method to validate the signature of a webhook:

```ts
import {BravoWebhookDefinition} from '@bscotch/bravo';
import {BravoEntities} from '@bscotch/bravo';

// Quick function alias to cut that path down a bit
const isValidWebhookSignature = BravoEntities.BravoWebhookDefinition.isValidWebhookSignature;

// Depending on how you capture the webhook event,
// you'll need to get the signature from the HTTP headers.
Expand All @@ -263,7 +266,7 @@ const payloadId = webhookPayload.payloadId;
const secret = 'your-secret';
const url = 'the-webhook-postTo-url';

const isValid = BravoWebhookDefinition.isValidWebhookSignature(
const isValid = isValidWebhookSignature(
url,
secret,
payloadId,
Expand Down
21 changes: 19 additions & 2 deletions src/lib/BravoClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ import {
findRequiredByField,
createIsMatchFilter,
stringsMatch,
generateRandomString,
} from './utility.js';
import { BravoCollection } from './entities/BravoCollection';
import { BravoUser } from '$/lib/entities/BravoUser';
import { BravoOrganization } from '$entities/BravoOrganization';
import { BravoWidget } from '$entities/BravoWidget.js';
import { BravoColumn } from './entities/BravoColumn.js';
import type { ArrayMatchFunction, RequiredBy } from '$/types/Utility.js';
import type {
ArrayMatchFunction,
PartialBy,
RequiredBy,
} from '$/types/Utility.js';
import { BravoCardInstance } from './entities/BravoCard.js';
import { BravoCustomFieldDefinition } from './entities/BravoCustomField.js';
import type { FavroResponse } from './clientLib/FavroResponse.js';
Expand Down Expand Up @@ -775,9 +780,21 @@ export class BravoClient extends FavroClient {
return await webhooks.find(createIsMatchFilter(name, 'name'));
}

/**
* Create a new webhook. If a secret is not provided, one will
* be generated for you. (Webhook secrets are available when
* fetching webhook definitions from the Favro API, so they
* do not need to managed separately.)
*/
async createWebhook(
options: Omit<FavroApi.WebhookDefinition.Model, 'webhookId'>,
options: PartialBy<
Omit<FavroApi.WebhookDefinition.Model, 'webhookId'>,
'secret'
>,
) {
if (!options.secret) {
options.secret = await generateRandomString(24, 'base64');
}
const res = (await this.requestWithReturnedEntities(
`webhooks`,
{
Expand Down
2 changes: 2 additions & 0 deletions src/types/Utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type AnyFunction = (...args: any[]) => any;
export type RequiredBy<T, K extends keyof T> = Omit<T, K> &
Required<Pick<T, K>>;

export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

export type ReplaceArrayIndices<
AnyArray extends any[],
ReplaceIndices extends number,
Expand Down

0 comments on commit 372ae73

Please sign in to comment.