-
Notifications
You must be signed in to change notification settings - Fork 36
feat(connections-setup): add ConnectionsSetup component #2484
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
Open
viktormarinho
wants to merge
21
commits into
main
Choose a base branch
from
feat/connections-setup
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
3b8770a
docs: add ConnectionsSetup component design
viktormarinho fda1126
docs: add ConnectionsSetup implementation plan
viktormarinho 1eefc72
feat(connections-setup): add registry-item and connection-poll query …
viktormarinho 6ee1225
feat(connections-setup): add slot resolution pure logic with tests
viktormarinho 64e9adc
fix(connections-setup): replace dynamic import with static import in …
viktormarinho 089de1b
fix(connections-setup): fix misleading test description in slot-resol…
viktormarinho 6d768c6
feat(connections-setup): add use-slot-resolution hook
viktormarinho 4f8b36b
fix(connections-setup): fix dead null-check and improve registry erro…
viktormarinho 9bec0c4
feat(connections-setup): add use-connection-poller hook
viktormarinho f903a8a
feat(connections-setup): add slot-done component
viktormarinho c992085
feat(connections-setup): add slot-install-form component
viktormarinho 722bcc2
feat(connections-setup): add slot-auth-oauth component
viktormarinho d94b896
feat(connections-setup): add slot-auth-token component
viktormarinho cb6a7be
fix(connections-setup): fix poller response shape, install ID, oauth …
viktormarinho 023147f
feat(connections-setup): add slot-card state machine component
viktormarinho 81c3f53
feat(connections-setup): fix auth phase bug, add root component and b…
viktormarinho 5b4eec2
fix(connections-setup): fix render-phase side effects and stuck auth …
viktormarinho 2cca6ea
test(connections-setup): add DOM component tests with @testing-librar…
viktormarinho ed8a2f4
fix(connections-setup): always check auth after poller goes active
viktormarinho d98b5ee
fix(connections-setup): detect OAuth need via oauth_config, not just …
viktormarinho 5e4a3b6
fix(connections-setup): remove staleTime:Infinity from auth check query
viktormarinho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
apps/mesh/src/web/components/connections-setup/connections-setup.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import { useState, useRef } from "react"; | ||
| import { SlotCard } from "./slot-card"; | ||
| import type { ConnectionSlot } from "./use-slot-resolution"; | ||
|
|
||
| export interface ConnectionsSetupProps { | ||
| slots: Record<string, ConnectionSlot>; | ||
| onComplete: (connections: Record<string, string>) => void; | ||
| } | ||
|
|
||
| export function ConnectionsSetup({ slots, onComplete }: ConnectionsSetupProps) { | ||
| const [completed, setCompleted] = useState<Record<string, string>>({}); | ||
| const wasAllDoneRef = useRef(false); | ||
|
|
||
| const handleSlotComplete = (slotId: string, connectionId: string) => { | ||
| setCompleted((prev) => { | ||
| const next = { ...prev }; | ||
| if (connectionId === "") { | ||
| delete next[slotId]; | ||
| } else { | ||
| next[slotId] = connectionId; | ||
| } | ||
| return next; | ||
| }); | ||
| }; | ||
|
|
||
| const allSlotIds = Object.keys(slots); | ||
| const allDone = | ||
| allSlotIds.length > 0 && allSlotIds.every((id) => completed[id]); | ||
|
|
||
| if (allDone && !wasAllDoneRef.current) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Calling Prompt for AI agents |
||
| wasAllDoneRef.current = true; | ||
| onComplete(completed); | ||
| } else if (!allDone) { | ||
| wasAllDoneRef.current = false; | ||
| } | ||
|
|
||
| return ( | ||
| <div className="space-y-3"> | ||
| {Object.entries(slots).map(([slotId, slot]) => ( | ||
| <SlotCard | ||
| key={slotId} | ||
| slot={slot} | ||
| onComplete={(connectionId) => | ||
| handleSlotComplete(slotId, connectionId) | ||
| } | ||
| /> | ||
| ))} | ||
| </div> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export { ConnectionsSetup } from "./connections-setup"; | ||
| export type { ConnectionsSetupProps } from "./connections-setup"; | ||
| export type { ConnectionSlot } from "./use-slot-resolution"; |
99 changes: 99 additions & 0 deletions
99
apps/mesh/src/web/components/connections-setup/slot-auth-oauth.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import { useState } from "react"; | ||
| import { toast } from "sonner"; | ||
| import { authenticateMcp, useConnectionActions } from "@decocms/mesh-sdk"; | ||
| import { useQueryClient } from "@tanstack/react-query"; | ||
| import { KEYS } from "@/web/lib/query-keys"; | ||
| import { Button } from "@deco/ui/components/button.tsx"; | ||
|
|
||
| interface SlotAuthOAuthProps { | ||
| connectionId: string; | ||
| providerName: string; | ||
| onAuthed: () => void; | ||
| } | ||
|
|
||
| export function SlotAuthOAuth({ | ||
| connectionId, | ||
| providerName, | ||
| onAuthed, | ||
| }: SlotAuthOAuthProps) { | ||
| const [isPending, setIsPending] = useState(false); | ||
| const actions = useConnectionActions(); | ||
| const queryClient = useQueryClient(); | ||
| const mcpProxyUrl = new URL(`/mcp/${connectionId}`, window.location.origin); | ||
|
|
||
| const handleAuthorize = async () => { | ||
| setIsPending(true); | ||
| try { | ||
| const { token, tokenInfo, error } = await authenticateMcp({ | ||
| connectionId, | ||
| }); | ||
|
|
||
| if (error || !token) { | ||
| toast.error(`Authorization failed: ${error ?? "Unknown error"}`); | ||
| return; | ||
| } | ||
|
|
||
| if (tokenInfo) { | ||
| try { | ||
| const response = await fetch( | ||
| `/api/connections/${connectionId}/oauth-token`, | ||
| { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| credentials: "include", | ||
| body: JSON.stringify({ | ||
| accessToken: tokenInfo.accessToken, | ||
| refreshToken: tokenInfo.refreshToken, | ||
| expiresIn: tokenInfo.expiresIn, | ||
| scope: tokenInfo.scope, | ||
| clientId: tokenInfo.clientId, | ||
| clientSecret: tokenInfo.clientSecret, | ||
| tokenEndpoint: tokenInfo.tokenEndpoint, | ||
| }), | ||
| }, | ||
| ); | ||
| if (!response.ok) { | ||
| await actions.update.mutateAsync({ | ||
| id: connectionId, | ||
| data: { connection_token: token }, | ||
| }); | ||
| } else { | ||
| // Trigger tool re-discovery | ||
| await actions.update.mutateAsync({ id: connectionId, data: {} }); | ||
| } | ||
| } catch (err) { | ||
| console.error("Error saving OAuth token:", err); | ||
| await actions.update.mutateAsync({ | ||
| id: connectionId, | ||
| data: { connection_token: token }, | ||
| }); | ||
| } | ||
| } else { | ||
| await actions.update.mutateAsync({ | ||
| id: connectionId, | ||
| data: { connection_token: token }, | ||
| }); | ||
| } | ||
|
|
||
| await queryClient.invalidateQueries({ | ||
| queryKey: KEYS.isMCPAuthenticated(mcpProxyUrl.href, null), | ||
| }); | ||
|
|
||
| toast.success("Authorization successful"); | ||
| onAuthed(); | ||
| } finally { | ||
| setIsPending(false); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="space-y-3"> | ||
| <p className="text-sm text-muted-foreground"> | ||
| Authorize Mesh to access {providerName} on your behalf. | ||
| </p> | ||
| <Button onClick={handleAuthorize} disabled={isPending} className="w-full"> | ||
| {isPending ? "Authorizing..." : `Authorize with ${providerName}`} | ||
| </Button> | ||
| </div> | ||
| ); | ||
| } |
71 changes: 71 additions & 0 deletions
71
apps/mesh/src/web/components/connections-setup/slot-auth-token.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { useForm } from "react-hook-form"; | ||
| import { zodResolver } from "@hookform/resolvers/zod"; | ||
| import { z } from "zod"; | ||
| import { useConnectionActions } from "@decocms/mesh-sdk"; | ||
| import { Button } from "@deco/ui/components/button.tsx"; | ||
| import { | ||
| Form, | ||
| FormControl, | ||
| FormField, | ||
| FormItem, | ||
| FormLabel, | ||
| FormMessage, | ||
| } from "@deco/ui/components/form.tsx"; | ||
| import { Input } from "@deco/ui/components/input.tsx"; | ||
|
|
||
| const tokenSchema = z.object({ | ||
| token: z.string().min(1, "Token is required"), | ||
| }); | ||
|
|
||
| type TokenFormData = z.infer<typeof tokenSchema>; | ||
|
|
||
| interface SlotAuthTokenProps { | ||
| connectionId: string; | ||
| onAuthed: () => void; | ||
| } | ||
|
|
||
| export function SlotAuthToken({ connectionId, onAuthed }: SlotAuthTokenProps) { | ||
| const actions = useConnectionActions(); | ||
|
|
||
| const form = useForm<TokenFormData>({ | ||
| resolver: zodResolver(tokenSchema), | ||
| defaultValues: { token: "" }, | ||
| }); | ||
|
|
||
| const handleSubmit = async (data: TokenFormData) => { | ||
| await actions.update.mutateAsync({ | ||
| id: connectionId, | ||
| data: { connection_token: data.token }, | ||
| }); | ||
| // Trigger tool re-discovery | ||
| await actions.update.mutateAsync({ id: connectionId, data: {} }); | ||
| onAuthed(); | ||
| }; | ||
|
|
||
| return ( | ||
| <Form {...form}> | ||
| <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-3"> | ||
| <FormField | ||
| control={form.control} | ||
| name="token" | ||
| render={({ field }) => ( | ||
| <FormItem> | ||
| <FormLabel>API Token</FormLabel> | ||
| <FormControl> | ||
| <Input type="password" placeholder="sk-..." {...field} /> | ||
| </FormControl> | ||
| <FormMessage /> | ||
| </FormItem> | ||
| )} | ||
| /> | ||
| <Button | ||
| type="submit" | ||
| disabled={actions.update.isPending} | ||
| className="w-full" | ||
| > | ||
| {actions.update.isPending ? "Saving..." : "Save token"} | ||
| </Button> | ||
| </form> | ||
| </Form> | ||
| ); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.