Files
outline/app/hooks/useAuthorizedSettingsConfig.ts
Tom Moor 10f86ed218 feat: Webhooks (#3691)
* Webhooks (#3607)

* Get the migration and the model setup. Also make the sample env file a bit easier to use. Now just requires setting a SECRET_KEY and besides that will boot up from the sample

* WIP: Start getting a Webhook page created. Just the skeleton state right now

* WIP: Getting a form created to create webhooks, need to bring in react-hook-forms now

* WIP: Get library installed and make TS happy

* Get a few checkboxes ready to go

* Get creating and destroying working with a decent start to a frontend

* Didn't mean to enable this

* Remove eslint and fix other random typescript issue

* Rename some events to be more realistic

* Revert these changes

* PR review comments around policies. Also make sure this inherits from IdModel so it actually gets an id

* Allow any admin on the team to edit webhooks

* Start sending some webhooks for some User events

* Make sure the URL is valid

* Start recording webhook deliveries

* Make sure to verify if the subscription is for the type of event we are looking at

* Refactor sending Webhooks and follow better webhook schema

This creates a presenter to unify the format of webhooks. We also
extract the sending of webhooks and recording their deliveries to a
method than can be used by each of the different event type methods

We also add a status to WebhookDelivery since we need to save the record
before we make the HTTP request to get its id. Then once we make the
request and get a response we can update the delivery with the HTTP info

* Turn off a subscription that has failed for the last 25 deliveries

* Get a first spec passing. Found a bug in my returning of promises so good to patch that up now

* This looks nicer

* Get some tests added for the processor

* Add cron task to delete older webhooks

* Add Document Events to the Processor

* Revisions, FileOperations and Collections

* Get all the server side events added to the processor and make Typescript make sure they are all accounted for

* Get all the events added to the Frontend and work on styling them a bit, still needs some love though

* Get UI styled up a bit

* Get events wired up for webhook subscriptions

* Get delete events working and test at least one variant of them

* Get deletes working and actually make sure to send the model id in the webhook

* Remove webhook secrets from this slice

* Add disabled label for subscriptions that are disabled

* Make sure to cascade the delete

* Reorg this file a bit

* Fix association

* I removed secret for the moment

* Apply Copy changes from PR Review

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Actually apply the copy changes

TIL that if you Resolve a conversation it _also_ removes the 'staged suggestion' from your list on Github

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/scenes/Settings/Webhooks.tsx

Missed this copy change before

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Add disabled as yellow badge

* Resolve frontend comments

* Fixup Schema a bit and remove the dependency on the subscription

* Add test to make sure we don't disable until there are enough failures, and fix code to actually do that. Also some test fixes from the json response shape changes

* Fix WebhookDeliveries to store the responses as Text instead of blobs

* Switch to text better for response bodies, this is using the helpers better and makes the code read better

* Move the logic to a task but run in through the processor cause the tests expect that right now, moving the tests over next

* Split up the tests and actually enqueue the events from the WebhookProcessor instead of doing them inline

* Allow any team admin to see any webhook subscription for the team

* Add the indexes based on our lookup patterns

* Run eslint --fix to fix auto correct issues from when I tried to use Github to merge copy changes

* Allow subscriptions to be edited after creation

* Types caught that I didn't add the new event to the webhook processor, also added it to the frontend here

* I think this will get these into the translations file

* Catch a few more translations, use styled components better and remove usage of webhook subscription in the copy

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* fix: tsc
fix: Document model payload empty

* fix: Revision webhook payload
Add custom UA for hooks

* Add webhooks icon, move under Integrations settings
Some spacing fixes

* Add actorId to webhook payloads

* Add View and ApiKey event types

* Spacing tweaks, fix team payload

* fix: Webhook not disabled after 25 failures

* fix: Enable webhook when editing if previously disabled

* fix: Correctly store response headers

* fix: Error in json/parsing/presentation results in hanging 'pending' webhook delivery

* fix: Awkward payload for users.invite webhook

* Add BaseEvent, ShareEvent

* fix: Add share events to form

* fix: Move webhook delivery cleanup to single DB call
Remove some unused abstraction

* Add user, collection, group context to membership webhook events
Some associated refactoring

Co-authored-by: Corey Alexander <coreyja@gmail.com>
2022-06-28 22:44:50 -07:00

214 lines
5.2 KiB
TypeScript

import {
NewDocumentIcon,
EmailIcon,
ProfileIcon,
PadlockIcon,
CodeIcon,
UserIcon,
GroupIcon,
LinkIcon,
TeamIcon,
BeakerIcon,
DownloadIcon,
WebhooksIcon,
} from "outline-icons";
import React from "react";
import { useTranslation } from "react-i18next";
import Details from "~/scenes/Settings/Details";
import Export from "~/scenes/Settings/Export";
import Features from "~/scenes/Settings/Features";
import Groups from "~/scenes/Settings/Groups";
import Import from "~/scenes/Settings/Import";
import Members from "~/scenes/Settings/Members";
import Notifications from "~/scenes/Settings/Notifications";
import Profile from "~/scenes/Settings/Profile";
import Security from "~/scenes/Settings/Security";
import Shares from "~/scenes/Settings/Shares";
import Slack from "~/scenes/Settings/Slack";
import Tokens from "~/scenes/Settings/Tokens";
import Webhooks from "~/scenes/Settings/Webhooks";
import Zapier from "~/scenes/Settings/Zapier";
import SlackIcon from "~/components/SlackIcon";
import ZapierIcon from "~/components/ZapierIcon";
import env from "~/env";
import isCloudHosted from "~/utils/isCloudHosted";
import useCurrentTeam from "./useCurrentTeam";
import usePolicy from "./usePolicy";
type SettingsGroups = "Account" | "Team" | "Integrations";
type SettingsPage =
| "Profile"
| "Notifications"
| "Api"
| "Details"
| "Security"
| "Features"
| "Members"
| "Groups"
| "Shares"
| "Import"
| "Export"
| "Webhooks"
| "Slack"
| "Zapier";
export type ConfigItem = {
name: string;
path: string;
icon: React.FC<any>;
component: () => JSX.Element;
enabled: boolean;
group: SettingsGroups;
};
type ConfigType = {
[key in SettingsPage]: ConfigItem;
};
const useAuthorizedSettingsConfig = () => {
const team = useCurrentTeam();
const can = usePolicy(team.id);
const { t } = useTranslation();
const config: ConfigType = React.useMemo(
() => ({
Profile: {
name: t("Profile"),
path: "/settings",
component: Profile,
enabled: true,
group: t("Account"),
icon: ProfileIcon,
},
Notifications: {
name: t("Notifications"),
path: "/settings/notifications",
component: Notifications,
enabled: true,
group: t("Account"),
icon: EmailIcon,
},
Api: {
name: t("API Tokens"),
path: "/settings/tokens",
component: Tokens,
enabled: can.createApiKey,
group: t("Account"),
icon: CodeIcon,
},
// Team group
Details: {
name: t("Details"),
path: "/settings/details",
component: Details,
enabled: can.update,
group: t("Team"),
icon: TeamIcon,
},
Security: {
name: t("Security"),
path: "/settings/security",
component: Security,
enabled: can.update,
group: t("Team"),
icon: PadlockIcon,
},
Features: {
name: t("Features"),
path: "/settings/features",
component: Features,
enabled: can.update,
group: t("Team"),
icon: BeakerIcon,
},
Members: {
name: t("Members"),
path: "/settings/members",
component: Members,
enabled: true,
group: t("Team"),
icon: UserIcon,
},
Groups: {
name: t("Groups"),
path: "/settings/groups",
component: Groups,
enabled: true,
group: t("Team"),
icon: GroupIcon,
},
Shares: {
name: t("Share Links"),
path: "/settings/shares",
component: Shares,
enabled: true,
group: t("Team"),
icon: LinkIcon,
},
Import: {
name: t("Import"),
path: "/settings/import",
component: Import,
enabled: can.manage,
group: t("Team"),
icon: NewDocumentIcon,
},
Export: {
name: t("Export"),
path: "/settings/export",
component: Export,
enabled: can.export,
group: t("Team"),
icon: DownloadIcon,
},
// Integrations
Webhooks: {
name: t("Webhooks"),
path: "/settings/webhooks",
component: Webhooks,
enabled: can.createWebhookSubscription,
group: t("Integrations"),
icon: WebhooksIcon,
},
Slack: {
name: "Slack",
path: "/settings/integrations/slack",
component: Slack,
enabled: can.update && (!!env.SLACK_CLIENT_ID || isCloudHosted),
group: t("Integrations"),
icon: SlackIcon,
},
Zapier: {
name: "Zapier",
path: "/settings/integrations/zapier",
component: Zapier,
enabled: can.update && isCloudHosted,
group: t("Integrations"),
icon: ZapierIcon,
},
}),
[
can.createApiKey,
can.createWebhookSubscription,
can.export,
can.manage,
can.update,
t,
]
);
const enabledConfigs = React.useMemo(
() =>
Object.keys(config).reduce(
(acc, key: SettingsPage) =>
config[key].enabled ? [...acc, config[key]] : acc,
[]
),
[config]
);
return enabledConfigs;
};
export default useAuthorizedSettingsConfig;