mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
Merge branch 'dev' into swiftyos/caching-pt2
This commit is contained in:
@@ -244,7 +244,7 @@ async def get_credential(
|
||||
return credential
|
||||
|
||||
|
||||
@router.post("/{provider}/credentials", status_code=201)
|
||||
@router.post("/{provider}/credentials", status_code=201, summary="Create Credentials")
|
||||
async def create_credentials(
|
||||
user_id: Annotated[str, Security(get_user_id)],
|
||||
provider: Annotated[
|
||||
|
||||
@@ -7,10 +7,8 @@ import { useMemo } from "react";
|
||||
import { CustomNode } from "./nodes/CustomNode";
|
||||
import { useCustomEdge } from "./edges/useCustomEdge";
|
||||
import CustomEdge from "./edges/CustomEdge";
|
||||
import { RightSidebar } from "../RIghtSidebar";
|
||||
|
||||
export const Flow = () => {
|
||||
// All these 3 are working perfectly
|
||||
const nodes = useNodeStore(useShallow((state) => state.nodes));
|
||||
const onNodesChange = useNodeStore(
|
||||
useShallow((state) => state.onNodesChange),
|
||||
@@ -20,7 +18,6 @@ export const Flow = () => {
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full dark:bg-slate-900">
|
||||
{/* Builder area - flexible width */}
|
||||
<div className="relative flex-1">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
@@ -36,9 +33,6 @@ export const Flow = () => {
|
||||
<NewControlPanel />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
<div className="w-[30%]">
|
||||
<RightSidebar />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ const CustomEdge = ({
|
||||
<EdgeLabelRenderer>
|
||||
<Button
|
||||
onClick={() => removeConnection(id)}
|
||||
className={`absolute z-10 min-w-0 p-1`}
|
||||
className={`absolute z-10 h-fit min-w-0 p-1`}
|
||||
variant="secondary"
|
||||
style={{
|
||||
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
|
||||
|
||||
@@ -32,8 +32,8 @@ export const CustomNode: React.FC<NodeProps<CustomNode>> = React.memo(
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-xl border border-slate-200/60 bg-gradient-to-br from-white to-slate-50/30 shadow-lg shadow-slate-900/5 backdrop-blur-sm",
|
||||
selected && "border-2 border-slate-200 shadow-2xl",
|
||||
"rounded-xl bg-gradient-to-br from-white to-slate-50/30 shadow-lg shadow-slate-900/5 ring-1 ring-slate-200/60 backdrop-blur-sm",
|
||||
selected && "shadow-2xl ring-2 ring-slate-200",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const OutputHandler = ({
|
||||
<div className="flex flex-col items-end justify-between gap-2 rounded-b-xl border-t border-slate-200/50 bg-white py-3.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="mr-4 p-0"
|
||||
className="mr-4 h-fit min-w-0 p-0 hover:border-transparent hover:bg-transparent"
|
||||
onClick={() => setIsOutputVisible(!isOutputVisible)}
|
||||
>
|
||||
<Text
|
||||
@@ -54,30 +54,27 @@ export const OutputHandler = ({
|
||||
|
||||
return shouldShow ? (
|
||||
<div key={key} className="relative flex items-center gap-2">
|
||||
<Text
|
||||
variant="body"
|
||||
className="flex items-center gap-2 font-medium text-slate-700"
|
||||
>
|
||||
{property?.description && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
style={{ marginLeft: 6, cursor: "pointer" }}
|
||||
aria-label="info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<InfoIcon />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{property?.description}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
{property?.description && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
style={{ marginLeft: 6, cursor: "pointer" }}
|
||||
aria-label="info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<InfoIcon />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{property?.description}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
<Text variant="body" className="text-slate-700">
|
||||
{property?.title || key}{" "}
|
||||
<Text variant="small" as="span" className={colorClass}>
|
||||
({displayType})
|
||||
</Text>
|
||||
</Text>
|
||||
<Text variant="small" as="span" className={colorClass}>
|
||||
({displayType})
|
||||
</Text>
|
||||
<NodeHandle id={key} isConnected={isConnected} side="right" />
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,42 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { FieldProps } from "@rjsf/utils";
|
||||
import { useCredentialField } from "./useCredentialField";
|
||||
import { filterCredentialsByProvider } from "./helpers";
|
||||
import { PlusIcon } from "@phosphor-icons/react";
|
||||
import { KeyIcon, PlusIcon } from "@phosphor-icons/react";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { SelectCredential } from "./SelectCredential";
|
||||
import { Skeleton } from "@/components/__legacy__/ui/skeleton";
|
||||
import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api";
|
||||
import { APIKeyCredentialsModal } from "./models/APIKeyCredentialModal/APIKeyCredentialModal";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
|
||||
export const CredentialsField = (props: FieldProps) => {
|
||||
const { formData = {}, onChange, required: _required, schema } = props;
|
||||
const { credentials, isCredentialListLoading } = useCredentialField();
|
||||
|
||||
const credentialProviders = schema.credentials_provider;
|
||||
const { credentials: filteredCredentials, exists: credentialsExists } =
|
||||
filterCredentialsByProvider(credentials, credentialProviders);
|
||||
const {
|
||||
credentials,
|
||||
isCredentialListLoading,
|
||||
supportsApiKey,
|
||||
supportsOAuth2,
|
||||
isAPIKeyModalOpen,
|
||||
setIsAPIKeyModalOpen,
|
||||
credentialsExists,
|
||||
} = useCredentialField({
|
||||
credentialSchema: schema as BlockIOCredentialsSubSchema,
|
||||
});
|
||||
|
||||
const setField = (key: string, value: any) =>
|
||||
onChange({ ...formData, [key]: value });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCredentialListLoading && credentials.length > 0 && !formData.id) {
|
||||
const latestCredential = credentials[credentials.length - 1];
|
||||
setField("id", latestCredential.id);
|
||||
}
|
||||
}, [isCredentialListLoading, credentials, formData.id]);
|
||||
|
||||
const handleCredentialCreated = (credentialId: string) => {
|
||||
setField("id", credentialId);
|
||||
};
|
||||
|
||||
if (isCredentialListLoading) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
@@ -31,7 +50,7 @@ export const CredentialsField = (props: FieldProps) => {
|
||||
<div className="flex flex-col gap-2">
|
||||
{credentialsExists && (
|
||||
<SelectCredential
|
||||
credentials={filteredCredentials}
|
||||
credentials={credentials}
|
||||
value={formData.id}
|
||||
onChange={(value) => setField("id", value)}
|
||||
disabled={false}
|
||||
@@ -40,10 +59,35 @@ export const CredentialsField = (props: FieldProps) => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* TODO : We need to add a modal to add a new credential */}
|
||||
<Button type="button" className="w-fit" size="small">
|
||||
<PlusIcon /> Add API Key
|
||||
</Button>
|
||||
<div>
|
||||
{supportsApiKey && (
|
||||
<>
|
||||
<APIKeyCredentialsModal
|
||||
schema={schema as BlockIOCredentialsSubSchema}
|
||||
open={isAPIKeyModalOpen}
|
||||
onClose={() => setIsAPIKeyModalOpen(false)}
|
||||
onSuccess={handleCredentialCreated}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
className="w-auto min-w-0"
|
||||
size="small"
|
||||
onClick={() => setIsAPIKeyModalOpen(true)}
|
||||
>
|
||||
<KeyIcon />
|
||||
<Text variant="body-medium" className="!text-white opacity-100">
|
||||
Add API key
|
||||
</Text>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{supportsOAuth2 && (
|
||||
<Button type="button" className="w-fit" size="small">
|
||||
<PlusIcon />
|
||||
Add OAuth2
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from "react";
|
||||
import { Select } from "@/components/atoms/Select/Select";
|
||||
import { CredentialsMetaResponse } from "@/app/api/__generated__/models/credentialsMetaResponse";
|
||||
import { KeyIcon } from "@phosphor-icons/react";
|
||||
import { ArrowSquareOutIcon, KeyIcon } from "@phosphor-icons/react";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import Link from "next/link";
|
||||
|
||||
type SelectCredentialProps = {
|
||||
credentials: CredentialsMetaResponse[];
|
||||
@@ -44,17 +46,24 @@ export const SelectCredential: React.FC<SelectCredentialProps> = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<Select
|
||||
label={label}
|
||||
id="select-credential"
|
||||
wrapperClassName="!mb-0"
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
size="small"
|
||||
hideLabel
|
||||
/>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<Select
|
||||
label={label}
|
||||
id="select-credential"
|
||||
wrapperClassName="!mb-0 flex-1"
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
size="small"
|
||||
hideLabel
|
||||
/>
|
||||
<Link href={`/profile/integrations`}>
|
||||
<Button variant="outline" size="icon" className="h-8 w-8 p-0">
|
||||
<ArrowSquareOutIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { CredentialsMetaResponse } from "@/app/api/__generated__/models/credentialsMetaResponse";
|
||||
// Need to replace these icons with phosphor icons
|
||||
import {
|
||||
FaDiscord,
|
||||
FaMedium,
|
||||
FaGithub,
|
||||
FaGoogle,
|
||||
FaHubspot,
|
||||
FaTwitter,
|
||||
} from "react-icons/fa";
|
||||
import { GoogleLogoIcon, KeyIcon, NotionLogoIcon } from "@phosphor-icons/react";
|
||||
|
||||
export const filterCredentialsByProvider = (
|
||||
credentials: CredentialsMetaResponse[] | undefined,
|
||||
@@ -45,3 +55,47 @@ export function isCredentialFieldSchema(schema: any): boolean {
|
||||
"credentials_provider" in schema
|
||||
);
|
||||
}
|
||||
|
||||
export const providerIcons: Partial<
|
||||
Record<string, React.FC<{ className?: string }>>
|
||||
> = {
|
||||
aiml_api: KeyIcon,
|
||||
anthropic: KeyIcon,
|
||||
apollo: KeyIcon,
|
||||
e2b: KeyIcon,
|
||||
github: FaGithub,
|
||||
google: GoogleLogoIcon,
|
||||
groq: KeyIcon,
|
||||
http: KeyIcon,
|
||||
notion: NotionLogoIcon,
|
||||
nvidia: KeyIcon,
|
||||
discord: FaDiscord,
|
||||
d_id: KeyIcon,
|
||||
google_maps: FaGoogle,
|
||||
jina: KeyIcon,
|
||||
ideogram: KeyIcon,
|
||||
linear: KeyIcon,
|
||||
medium: FaMedium,
|
||||
mem0: KeyIcon,
|
||||
ollama: KeyIcon,
|
||||
openai: KeyIcon,
|
||||
openweathermap: KeyIcon,
|
||||
open_router: KeyIcon,
|
||||
llama_api: KeyIcon,
|
||||
pinecone: KeyIcon,
|
||||
enrichlayer: KeyIcon,
|
||||
slant3d: KeyIcon,
|
||||
screenshotone: KeyIcon,
|
||||
smtp: KeyIcon,
|
||||
replicate: KeyIcon,
|
||||
reddit: KeyIcon,
|
||||
fal: KeyIcon,
|
||||
revid: KeyIcon,
|
||||
twitter: FaTwitter,
|
||||
unreal_speech: KeyIcon,
|
||||
exa: KeyIcon,
|
||||
hubspot: FaHubspot,
|
||||
smartlead: KeyIcon,
|
||||
todoist: KeyIcon,
|
||||
zerobounce: KeyIcon,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Input } from "@/components/atoms/Input/Input";
|
||||
import { Button } from "@/components/atoms/Button/Button";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import {
|
||||
Form,
|
||||
FormDescription,
|
||||
FormField,
|
||||
} from "@/components/__legacy__/ui/form";
|
||||
import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types"; // we need to find a way to replace it with autogenerated types
|
||||
import { useAPIKeyCredentialsModal } from "./useAPIKeyCredentialsModal";
|
||||
import { toDisplayName } from "../../helpers";
|
||||
|
||||
type Props = {
|
||||
schema: BlockIOCredentialsSubSchema;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: (credentialId: string) => void;
|
||||
};
|
||||
|
||||
export function APIKeyCredentialsModal({
|
||||
schema,
|
||||
open,
|
||||
onClose,
|
||||
onSuccess,
|
||||
}: Props) {
|
||||
const { form, isLoading, schemaDescription, onSubmit, provider } =
|
||||
useAPIKeyCredentialsModal({ schema, onClose, onSuccess });
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={`Add new API key for ${toDisplayName(provider) ?? ""}`}
|
||||
controlled={{
|
||||
isOpen: open,
|
||||
set: (isOpen) => {
|
||||
if (!isOpen) onClose();
|
||||
},
|
||||
}}
|
||||
onClose={onClose}
|
||||
styling={{
|
||||
maxWidth: "25rem",
|
||||
}}
|
||||
>
|
||||
<Dialog.Content>
|
||||
{schemaDescription && (
|
||||
<p className="mb-4 text-sm text-zinc-600">{schemaDescription}</p>
|
||||
)}
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="apiKey"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Input
|
||||
id="apiKey"
|
||||
label="API Key"
|
||||
type="password"
|
||||
placeholder="Enter API key..."
|
||||
size="small"
|
||||
hint={
|
||||
schema.credentials_scopes ? (
|
||||
<FormDescription>
|
||||
Required scope(s) for this block:{" "}
|
||||
{schema.credentials_scopes?.map((s, i, a) => (
|
||||
<span key={i}>
|
||||
<code className="text-xs font-bold">{s}</code>
|
||||
{i < a.length - 1 && ", "}
|
||||
</span>
|
||||
))}
|
||||
</FormDescription>
|
||||
) : null
|
||||
}
|
||||
{...field}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
id="title"
|
||||
label="Name"
|
||||
type="text"
|
||||
placeholder="Enter a name for this API key..."
|
||||
size="small"
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="expiresAt"
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
id="expiresAt"
|
||||
label="Expiration Date"
|
||||
type="datetime-local"
|
||||
placeholder="Select expiration date..."
|
||||
size="small"
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit" size="small" className="min-w-68">
|
||||
Save & use this API key
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { z } from "zod";
|
||||
import { useForm, type UseFormReturn } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types";
|
||||
import {
|
||||
getGetV1ListCredentialsQueryKey,
|
||||
usePostV1CreateCredentials,
|
||||
} from "@/app/api/__generated__/endpoints/integrations/integrations";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { APIKeyCredentials } from "@/app/api/__generated__/models/aPIKeyCredentials";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { PostV1CreateCredentials201 } from "@/app/api/__generated__/models/postV1CreateCredentials201";
|
||||
|
||||
export type APIKeyFormValues = {
|
||||
apiKey: string;
|
||||
title: string;
|
||||
expiresAt?: string;
|
||||
};
|
||||
|
||||
type useAPIKeyCredentialsModalType = {
|
||||
schema: BlockIOCredentialsSubSchema;
|
||||
onClose: () => void;
|
||||
onSuccess: (credentialId: string) => void;
|
||||
};
|
||||
|
||||
export function useAPIKeyCredentialsModal({
|
||||
schema,
|
||||
onClose,
|
||||
onSuccess,
|
||||
}: useAPIKeyCredentialsModalType): {
|
||||
form: UseFormReturn<APIKeyFormValues>;
|
||||
isLoading: boolean;
|
||||
provider: string;
|
||||
schemaDescription?: string;
|
||||
onSubmit: (values: APIKeyFormValues) => Promise<void>;
|
||||
} {
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutateAsync: createCredentials, isPending: isCreatingCredentials } =
|
||||
usePostV1CreateCredentials({
|
||||
mutation: {
|
||||
onSuccess: async (response) => {
|
||||
const credentialId = (response.data as PostV1CreateCredentials201)
|
||||
?.id;
|
||||
onClose();
|
||||
form.reset();
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Credentials created successfully",
|
||||
variant: "default",
|
||||
});
|
||||
|
||||
await queryClient.refetchQueries({
|
||||
queryKey: getGetV1ListCredentialsQueryKey(),
|
||||
});
|
||||
|
||||
if (credentialId && onSuccess) {
|
||||
onSuccess(credentialId);
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to create credentials.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formSchema = z.object({
|
||||
apiKey: z.string().min(1, "API Key is required"),
|
||||
title: z.string().min(1, "Name is required"),
|
||||
expiresAt: z.string().optional(),
|
||||
});
|
||||
|
||||
const form = useForm<APIKeyFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
apiKey: "",
|
||||
title: "",
|
||||
expiresAt: "",
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(values: APIKeyFormValues) {
|
||||
const expiresAt = values.expiresAt
|
||||
? new Date(values.expiresAt).getTime() / 1000
|
||||
: undefined;
|
||||
|
||||
createCredentials({
|
||||
provider: schema.credentials_provider[0],
|
||||
data: {
|
||||
provider: schema.credentials_provider[0],
|
||||
type: "api_key",
|
||||
api_key: values.apiKey,
|
||||
title: values.title,
|
||||
expires_at: expiresAt,
|
||||
} as APIKeyCredentials,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
form,
|
||||
isLoading: isCreatingCredentials,
|
||||
provider: schema.credentials_provider[0],
|
||||
schemaDescription: schema.description,
|
||||
onSubmit,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
import { useGetV1ListCredentials } from "@/app/api/__generated__/endpoints/integrations/integrations";
|
||||
import { CredentialsMetaResponse } from "@/app/api/__generated__/models/credentialsMetaResponse";
|
||||
import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api";
|
||||
import { useState } from "react";
|
||||
import { filterCredentialsByProvider } from "./helpers";
|
||||
|
||||
export const useCredentialField = ({
|
||||
credentialSchema,
|
||||
}: {
|
||||
credentialSchema: BlockIOCredentialsSubSchema; // Here we are using manual typing, we need to fix it with automatic one
|
||||
}) => {
|
||||
const [isAPIKeyModalOpen, setIsAPIKeyModalOpen] = useState(false);
|
||||
|
||||
export const useCredentialField = () => {
|
||||
// Fetch all the credentials from the backend
|
||||
// We will save it in cache for 10 min, if user edits the credential, we will invalidate the cache
|
||||
// Whenever user adds a block, we filter the credentials list and check if this block's provider is in the list
|
||||
@@ -14,8 +23,21 @@ export const useCredentialField = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const supportsApiKey = credentialSchema.credentials_types.includes("api_key");
|
||||
const supportsOAuth2 = credentialSchema.credentials_types.includes("oauth2");
|
||||
|
||||
const credentialProviders = credentialSchema.credentials_provider;
|
||||
const { credentials: filteredCredentials, exists: credentialsExists } =
|
||||
filterCredentialsByProvider(credentials, credentialProviders);
|
||||
|
||||
return {
|
||||
credentials,
|
||||
credentials: filteredCredentials,
|
||||
isCredentialListLoading,
|
||||
supportsApiKey,
|
||||
supportsOAuth2,
|
||||
isAPIKeyModalOpen,
|
||||
setIsAPIKeyModalOpen,
|
||||
credentialsExists,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
"post": {
|
||||
"tags": ["v1", "integrations"],
|
||||
"summary": "Create Credentials",
|
||||
"operationId": "postV1CreateCredentials",
|
||||
"operationId": "postV1Create credentials",
|
||||
"security": [{ "HTTPBearerJWT": [] }],
|
||||
"parameters": [
|
||||
{
|
||||
@@ -246,7 +246,7 @@
|
||||
"host_scoped": "#/components/schemas/HostScopedCredentials-Output"
|
||||
}
|
||||
},
|
||||
"title": "Response Postv1Createcredentials"
|
||||
"title": "Response Postv1Create Credentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user