Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ui/feat/slack-oauth-app #307

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions ui/admin/app/components/composed/CopyText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@ export function CopyText({
text,
displayText = text,
className,
holdStatusDelay,
}: {
text: string;
displayText?: string;
className?: string;
holdStatusDelay?: number;
}) {
const [isCopied, setIsCopied] = useState(false);

useEffect(() => {
if (!isCopied) return;
if (!isCopied || !holdStatusDelay) return;

const timeout = setTimeout(() => setIsCopied(false), 10000);
const timeout = setTimeout(() => setIsCopied(false), holdStatusDelay);

return () => clearTimeout(timeout);
}, [isCopied]);
}, [isCopied, holdStatusDelay]);

return (
<div
Expand Down
4 changes: 4 additions & 0 deletions ui/admin/app/components/oauth-apps/ConfigureOAuthApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export function ConfigureOAuthApp({ type }: { type: OAuthProvider }) {
await OauthAppService.createOauthApp({
type,
refName: type,
global: true,
integration: type,
...data,
});

Expand All @@ -55,6 +57,8 @@ export function ConfigureOAuthApp({ type }: { type: OAuthProvider }) {
await OauthAppService.updateOauthApp(customApp.id, {
type: customApp.type,
refName: customApp.refName,
global: customApp.global,
integration: customApp.integration,
...data,
});

Expand Down
19 changes: 15 additions & 4 deletions ui/admin/app/components/oauth-apps/OAuthAppForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,34 @@ export function OAuthAppForm({ type, onSubmit, isLoading }: OAuthAppFormProps) {
}
case "copy": {
return (
<div className="w-full flex justify-center">
<div className="flex justify-center">
<CopyText
text={step.text}
className="mx-8 w-auto max-w-fit justify-center"
className="w-auto max-w-fit justify-center"
/>
</div>
);
}
case "sectionGroup": {
return (
<Accordion type="single" collapsible>
<Accordion
type="multiple"
defaultValue={step.sections
.filter((s) => s.defaultOpen)
.map((_, i) => i.toString())}
>
{step.sections.map((section, index) => (
<AccordionItem key={index} value={index.toString()}>
<AccordionTrigger>
{section.title}
</AccordionTrigger>
<AccordionContent>

<AccordionContent
className={cn("flex flex-col gap-1", {
"flex-row justify-center flex-wrap gap-2":
section.displayStepsInline,
})}
>
{section.steps.map(renderStep)}
</AccordionContent>
</AccordionItem>
Expand Down
1 change: 1 addition & 0 deletions ui/admin/app/lib/model/oauthApps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type OAuthAppParams = {
export type OAuthAppBase = OAuthAppParams & {
type: OAuthProvider;
refName: string;
global: boolean;
};

export type OAuthApp = EntityMeta &
Expand Down
7 changes: 6 additions & 1 deletion ui/admin/app/lib/model/oauthApps/oauth-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ export type OAuthFormStep<T extends object = Record<string, string>> =
| { type: "copy"; text: string }
| {
type: "sectionGroup";
sections: { title: string; steps: OAuthFormStep[] }[];
sections: {
title: string;
steps: OAuthFormStep[];
displayStepsInline?: boolean;
defaultOpen?: boolean;
}[];
};

export type OAuthAppSpec = {
Expand Down
2 changes: 1 addition & 1 deletion ui/admin/app/lib/model/oauthApps/providers/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const steps: OAuthFormStep<typeof schema.shape>[] = [
},
{
type: "copy",
text: BaseUrl,
text: BaseUrl(),
},

{
Expand Down
107 changes: 88 additions & 19 deletions ui/admin/app/lib/model/oauthApps/providers/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,25 @@ const schema = z.object({
clientSecret: z.string().min(1, "Client Secret is required"),
});

const scopes = [
"https://www.googleapis.com/auth/userinfo.email",
"openid",
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/documents.readonly",
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.compose",
];

const enabledApis = ["Google Sheets", "Google Docs", "Gmail"];

const steps: OAuthFormStep<typeof schema.shape>[] = [
{
type: "markdown",
text:
"### Step 1: Create a new Google Project\n" +
"- Navigate to your [Google API Console](https://console.cloud.google.com/apis/credentials).\n" +
"- Navigate to the [Credentials](https://console.cloud.google.com/apis/credentials) section of the APIs & Serivces page in your [Google API Dashboard](https://console.cloud.google.com).\n" +
"- If you already have a Google Project Setup, skip to Step 2.",
},
{
Expand All @@ -39,36 +52,55 @@ const steps: OAuthFormStep<typeof schema.shape>[] = [
{
type: "markdown",
text:
"### Step 2: Configure your OAuth Consent Screen\n" +
"If you have already configured your OAuth Consent Screen, skip to Step 3.",
"### Step 2: Enable APIs and Services.\n" +
"- From the [Google API Dashboard](https://console.cloud.google.com/apis/dashboard), select your project from the dropdown in the top left, and click on the `+ ENABLE APIS AND SERVICES` button.\n" +
"- Search for each of the required APIs and click on the `Enable` button .\n" +
"- Repeat this process for each of the required APIs listed below.\n",
},
{
type: "sectionGroup",
sections: [
{
title: "How do I configure my OAuth Consent Screen?",
title: "Enable Apis: ",
displayStepsInline: true,
defaultOpen: true,
steps: enabledApis.map((api) => ({
type: "copy",
text: api,
})),
},
],
},
{
type: "markdown",
text:
"### Step 3: Configure your OAuth Consent Screen\n" +
"Select one of the options below.",
},
{
type: "sectionGroup",
sections: [
{
title: "I have not already configured my OAuth Consent Screen\n",
steps: [
{
type: "markdown",
text:
"- Click on the `OAuth consent screen` menu item on the left nav.\n" +
"- Select the `User type`.\n" +
"- Click on the `Create` button. (You will be redirected to the `Edit app registration` screen.)\n" +
"- Enter your `App Name`, `Support Email`, and optionally upload an image to the `App Logo` field.\n" +
"- Under the `App Domain` section, add the url below to the `Application home page` field:\n",
},
{
type: "copy",
text: DomainUrl,
"- Select the `User type` (Recommended: `Internal`).\n" +
" - If you need to use `External` you can read more about it [here](https://support.google.com/cloud/answer/10311615#user-type).\n" +
"- Click on the `Create` button. (You will be redirected to the `Edit app registration` screen.)\n",
},
],
},
{
title: "I've already configured my OAuth Consent Screen",
steps: [
{
type: "markdown",
text:
"- Provide an email address to the `Developer contact information` field.\n" +
"- Click on the `SAVE AND CONTINUE` button. (You will be redirected to the `Scopes` section.)\n" +
"#### Scopes\n" +
"- If you'd like to add scopes to your application, click on the `Add or remove scopes` button, select the scopes you'd like to add and click on the `Update` button.\n" +
"- Click on the `SAVE AND CONTINUE` button.\n",
"- Click on the `OAuth consent screen` menu item on the left nav.\n" +
"- Click on the `Edit App` button.\n",
},
],
},
Expand All @@ -77,7 +109,44 @@ const steps: OAuthFormStep<typeof schema.shape>[] = [
{
type: "markdown",
text:
"### Step 3: Create OAuth Credentials\n" +
"- (If you haven't already) Enter your `App Name`, `Support Email`, and optionally upload an image to the `App Logo` field.\n" +
"- Under the `App Domain` section, add the url below to the `Application home page` field:\n",
},
{
type: "copy",
text: DomainUrl(),
},
{
type: "markdown",
text:
"- Provide an email address to the `Developer contact information` field.\n" +
"- Click on the `SAVE AND CONTINUE` button. (You will be redirected to the `Scopes` section.)\n" +
"#### Scopes\n" +
"- Click on the `Add or remove scopes` button, then add the following scopes listed below.\n" +
"- Click on the `UPDATE` button when finished.\n",
},
{
type: "sectionGroup",
sections: [
{
title: "Scopes: ",
displayStepsInline: true,
defaultOpen: true,
steps: scopes.map((scope) => ({
type: "copy",
text: scope,
})),
},
],
},
{
type: "markdown",
text: "- Click on the `SAVE AND CONTINUE` button to move on to Step 4.\n",
},
{
type: "markdown",
text:
"### Step 4: Create OAuth Credentials\n" +
"- Select the `Credentials` section from the left nav.\n" +
"- Click on the `+ CREATE CREDENTIALS` button and select the `OAuth client ID` option.\n" +
"- Select the `Web application` option from the `Application type` dropdown.\n" +
Expand All @@ -97,7 +166,7 @@ const steps: OAuthFormStep<typeof schema.shape>[] = [
{
type: "markdown",
text:
"### Step 4: Register your OAuth App in Otto\n" +
"### Step 5: Register your OAuth App in Otto\n" +
"With the credentials you just created, register your OAuth App in Otto by entering the `Client ID` and `Client Secret` into the fields below and clicking on the `Submit` button.",
},
{ type: "input", input: "clientID", label: "Client ID" },
Expand Down
112 changes: 109 additions & 3 deletions ui/admin/app/lib/model/oauthApps/providers/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,126 @@ import { z } from "zod";

import { assetUrl } from "~/lib/utils";

import { OAuthAppSpec } from "../oauth-helpers";
import { OAuthAppSpec, OAuthFormStep, getOAuthLinks } from "../oauth-helpers";

const schema = z.object({
clientID: z.string().min(1, "Client ID is required"),
clientSecret: z.string().min(1, "Client Secret is required"),
});

const scopes = [
"channels:history",
"groups:history",
"im:history",
"mpim:history",
"channels:read",
"files:read",
"im:read",
"search:read",
"team:read",
"users:read",
"groups:read",
"chat:write",
"groups:write",
"mpim:write",
"im:write",
];
// any changes to the OAuth & Permissions section will require the user to click `Reinstall to <Slack App Name>`

const steps: OAuthFormStep<typeof schema.shape>[] = [
{
type: "markdown",
text:
"All steps will be performed on the [Slack API Dashboard](https://api.slack.com/apps).\n\n" +
"### Step 1: Create a Slack App\n" +
"If you've already created a Slack app, you can skip this step.\n",
},

{
type: "sectionGroup",
sections: [
{
title: "How do I create a Slack App?",
steps: [
{
type: "markdown",
text:
"- From the [Slack API Dashboard](https://api.slack.com/apps), create a new app and select `From scratch`\n" +
"- Give your app a `Name` and select a `Workspace`\n" +
"- Click `Create`\n",
},
],
},
],
},
{
type: "markdown",
text:
"### Step 2: Add the Redirect URL\n" +
"- From the [Slack API Dashboard](https://api.slack.com/apps), click on your app and select `OAuth & Permissions`\n" +
"- In the `Redirect URLs` section, click `Add New Redirect URL`\n" +
"- Add the following URL: ",
},
{
type: "copy",
text: getOAuthLinks("slack").redirectURL,
},
{
type: "markdown",
text: "- Click `Save URLs` to save the changes.\n",
},
{
type: "markdown",
text:
"### Step 3: Add Scopes\n" +
"- Navigate to the `OAuth & Permissions` tab from the sidebar.\n" +
"- Locate the `User Token Scopes` section and add the following scopes:\n",
},
{
type: "sectionGroup",
sections: [
{
title: "Scopes: ",
displayStepsInline: true,
defaultOpen: true,
steps: scopes.map(
(scope) =>
({
type: "copy",
text: scope,
}) as OAuthFormStep<typeof schema.shape>
),
},
],
},
{
type: "markdown",
text:
"### Step 4: Install the App\n" +
"- Navigate to the `OAuth & Permissions` tab from the sidebar.\n" +
"- Click on the `Install App to Workspace` (or `Reinstall to <App Name>` if it's already installed) button.\n",
},
{
type: "markdown",
text:
"### Step 5: Register OAuth App in Otto\n" +
"Click the `Basic Information` section in the side nav, locate the `Client ID` and `Client Secret` fields, copy/paste them into the form below, and click `Submit`.\n",
},
{ type: "input", input: "clientID", label: "Client ID" },
{
type: "input",
input: "clientSecret",
label: "Client Secret",
inputType: "password",
},
];

export const SlackOAuthApp = {
schema,
refName: "slack",
type: "slack",
displayName: "Slack",
logo: assetUrl("/assets/slack_logo_light.png"),
darkLogo: assetUrl("/assets/slack_logo_dark.png"),
steps: [],
disableConfiguration: true,
steps,
} satisfies OAuthAppSpec;
Loading