Skip to content

Commit

Permalink
feat(ui): Add full webhook url with runner url + add copy for action …
Browse files Browse the repository at this point in the history
…inputs
  • Loading branch information
daryllimyt committed Mar 24, 2024
1 parent 7180d12 commit 636b881
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 43 deletions.
121 changes: 83 additions & 38 deletions frontend/src/components/workspace/action-panel/form.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { useWorkflowBuilder } from "@/providers/builder"
import { useSession } from "@/providers/session"
import { zodResolver } from "@hookform/resolvers/zod"
import { CopyIcon } from "@radix-ui/react-icons"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { CircleIcon, DeleteIcon, Save } from "lucide-react"
import { useFieldArray, useForm } from "react-hook-form"
import {
FieldValues,
useFieldArray,
useForm,
UseFormReturn,
} from "react-hook-form"
import SyntaxHighlighter from "react-syntax-highlighter"
import { atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs"
import { z } from "zod"

import { Action, ActionType } from "@/types/schemas"
import { getActionById, updateAction } from "@/lib/flow"
import { cn, undoSlugify } from "@/lib/utils"
import { cn, copyToClipboard, undoSlugify } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Expand Down Expand Up @@ -42,6 +48,7 @@ import { FormLoading } from "@/components/loading/form"
import { AlertNotification } from "@/components/notifications"
import { ActionNodeType } from "@/components/workspace/action-node"
import {
ActionFieldOption,
baseActionSchema,
getSubActionSchema,
} from "@/components/workspace/action-panel/schemas"
Expand Down Expand Up @@ -165,6 +172,7 @@ export function ActionForm({
</div>
)
}

// Loading state to defend in a user friendly way
// against undefined schemas or data
if (isLoading) {
Expand Down Expand Up @@ -300,10 +308,11 @@ export function ActionForm({
available for all actions.
</p>
</div>
<div className="capitalize">
<div className="space-y-2 capitalize">
{Object.entries(fieldSchema).map(
([inputKey, inputOption]) => {
const typedKey = inputKey as keyof Schema

return (
<FormField
key={inputKey}
Expand All @@ -315,13 +324,11 @@ export function ActionForm({
return (
<FormItem>
<FormLabel className="text-xs">
{undoSlugify(inputKey)}
{inputOption.optional && (
<span className="text-muted-foreground">
{" "}
(Optional)
</span>
)}
<FormLabelInner
inputKey={inputKey}
inputOption={inputOption}
form={form}
/>
</FormLabel>
<FormControl>
<Select
Expand Down Expand Up @@ -366,13 +373,11 @@ export function ActionForm({
return (
<FormItem>
<FormLabel className="text-xs">
{undoSlugify(inputKey)}
{inputOption.optional && (
<span className="text-muted-foreground">
{" "}
(Optional)
</span>
)}
<FormLabelInner
inputKey={inputKey}
inputOption={inputOption}
form={form}
/>
</FormLabel>
<FormControl>
<Textarea
Expand All @@ -392,13 +397,11 @@ export function ActionForm({
return (
<FormItem>
<FormLabel className="text-xs">
{undoSlugify(inputKey)}
{inputOption.optional && (
<span className="text-muted-foreground">
{" "}
(Optional)
</span>
)}
<FormLabelInner
inputKey={inputKey}
inputOption={inputOption}
form={form}
/>
</FormLabel>
<FormControl>
<pre>
Expand Down Expand Up @@ -428,13 +431,11 @@ export function ActionForm({
return (
<FormItem>
<FormLabel className="text-xs">
{undoSlugify(inputKey)}
{inputOption.optional && (
<span className="text-muted-foreground">
{" "}
(Optional)
</span>
)}
<FormLabelInner
inputKey={inputKey}
inputOption={inputOption}
form={form}
/>
</FormLabel>
<div className="flex flex-col space-y-2">
{fields.map((field, index) => {
Expand Down Expand Up @@ -484,13 +485,11 @@ export function ActionForm({
return (
<FormItem>
<FormLabel className="text-xs">
{undoSlugify(inputKey)}
{inputOption.optional && (
<span className="text-muted-foreground">
{" "}
(Optional)
</span>
)}
<FormLabelInner
inputKey={inputKey}
inputOption={inputOption}
form={form}
/>
</FormLabel>
<FormControl>
<Input
Expand Down Expand Up @@ -550,3 +549,49 @@ export function ActionForm({
</Form>
)
}

function FormLabelInner<T extends FieldValues>({
inputKey,
inputOption,
form,
}: {
inputKey: string
inputOption: ActionFieldOption
form: UseFormReturn<T>
}) {
const typedKey = inputKey as keyof T
return (
<div className="flex items-center space-x-2">
<span>{undoSlugify(inputKey)}</span>
{inputOption.optional && (
<span className="text-muted-foreground"> (Optional)</span>
)}
{inputOption.copyable && (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
className="m-0 h-4 w-4 p-0"
onClick={() => {
copyToClipboard({
// @ts-ignore
value: form.getValues(typedKey),
message: "Copied URL to clipboard",
})
toast({
title: "Copied to clipboard",
// @ts-ignore
description: `Copied ${typedKey} to clipboard.`,
})
}}
>
<CopyIcon className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Copy</TooltipContent>
</Tooltip>
)}
</div>
)
}
3 changes: 2 additions & 1 deletion frontend/src/components/workspace/action-panel/schemas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export interface ActionFieldOption {
placeholder?: string
disabled?: boolean
optional?: boolean
copyable?: boolean
}

export interface ActionFieldSchema {
Expand Down Expand Up @@ -153,13 +154,13 @@ For example:

const actionFieldSchemas: Partial<AllActionFieldSchemas> = {
webhook: {
url: { type: "input", placeholder: "The allowed domain." },
method: {
type: "select",
options: ["GET", "POST"],
},
path: { type: "input", disabled: true },
secret: { type: "input", disabled: true },
url: { type: "input", disabled: true, copyable: true },
},
http_request: {
url: { type: "input" },
Expand Down
10 changes: 7 additions & 3 deletions tracecat/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,11 @@ def get_action(
# Precompute webhook response
# Alias webhook.id as path
if action.type.lower() == "webhook":
webhook_response = search_webhooks(
webhook = search_webhooks(
role=role,
params=SearchWebhooksParams(action_id=action.id),
)
inputs |= {"path": webhook_response.id, "secret": webhook_response.secret}
inputs.update(path=webhook.id, secret=webhook.secret, url=webhook.url)
return ActionResponse(
id=action.id,
type=action.type,
Expand Down Expand Up @@ -759,6 +759,7 @@ def list_webhooks(
path=webhook.path,
action_id=webhook.action_id,
workflow_id=webhook.workflow_id,
url=webhook.url,
)
for webhook in webhooks
]
Expand Down Expand Up @@ -786,12 +787,13 @@ def create_webhook(
action_id=webhook.action_id,
workflow_id=webhook.workflow_id,
secret=webhook.secret,
url=webhook.url,
)


@app.get("/webhooks/{webhook_id}")
def get_webhook(
role: Annotated[Role, Depends(authenticate_user)],
role: Annotated[Role, Depends(authenticate_user_or_service)],
webhook_id: str,
) -> WebhookResponse:
with Session(engine) as session:
Expand All @@ -811,6 +813,7 @@ def get_webhook(
secret=webhook.secret,
action_id=webhook.action_id,
workflow_id=webhook.workflow_id,
url=webhook.url,
)
return webhook_response

Expand Down Expand Up @@ -857,6 +860,7 @@ def search_webhooks(
secret=webhook.secret,
action_id=webhook.action_id,
workflow_id=webhook.workflow_id,
url=webhook.url,
)
return webhook_response

Expand Down
8 changes: 7 additions & 1 deletion tracecat/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from tracecat import auth
from tracecat.auth import decrypt_key, encrypt_key
from tracecat.config import TRACECAT__APP_ENV
from tracecat.config import TRACECAT__APP_ENV, TRACECAT__RUNNER_URL
from tracecat.labels.mitre import get_mitre_tactics_techniques

STORAGE_PATH = Path(
Expand Down Expand Up @@ -189,6 +189,7 @@ class Webhook(Resource, table=True):
default_factory=lambda: uuid4().hex,
primary_key=True,
description="Webhook path",
alias="path",
)
action_id: str | None = Field(foreign_key="action.id")
workflow_id: str | None = Field(foreign_key="workflow.id")
Expand All @@ -199,6 +200,11 @@ class Webhook(Resource, table=True):
def secret(self) -> str:
return auth.compute_hash(self.id)

@computed_field
@property
def url(self) -> str:
return f"{TRACECAT__RUNNER_URL}/webhook/{self.id}/{self.secret}"


def create_db_engine() -> Engine:
if TRACECAT__APP_ENV == "prod":
Expand Down
1 change: 1 addition & 0 deletions tracecat/types/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class WebhookResponse(BaseModel):
secret: str
action_id: str
workflow_id: str
url: str


class GetWebhookParams(BaseModel):
Expand Down

0 comments on commit 636b881

Please sign in to comment.