{
+ field.onChange(value);
+ if (onChange) {
+ onChange(value);
+ }
+ }}
>
Using IAM Role
diff --git a/ui/components/providers/workflow/forms/select-via-aws/index.ts b/ui/components/providers/workflow/forms/select-via-aws/index.ts
new file mode 100644
index 00000000000..8dd9822258b
--- /dev/null
+++ b/ui/components/providers/workflow/forms/select-via-aws/index.ts
@@ -0,0 +1 @@
+export * from "./select-via-aws";
diff --git a/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx b/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx
new file mode 100644
index 00000000000..59197bb40ef
--- /dev/null
+++ b/ui/components/providers/workflow/forms/select-via-aws/select-via-aws.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useForm } from "react-hook-form";
+
+import { Form } from "@/components/ui/form";
+
+import { RadioGroupAWSViaCredentialsForm } from "../radio-group-aws-via-credentials-form";
+
+interface SelectViaAWSProps {
+ initialVia?: string;
+}
+
+export const SelectViaAWS = ({ initialVia }: SelectViaAWSProps) => {
+ const router = useRouter();
+ const form = useForm({
+ defaultValues: {
+ awsCredentialsType: initialVia || "",
+ },
+ });
+
+ const handleSelectionChange = (value: string) => {
+ const url = new URL(window.location.href);
+ url.searchParams.set("via", value);
+ router.push(url.toString());
+ };
+
+ return (
+
+ );
+};
diff --git a/ui/components/providers/workflow/forms/test-connection-form.tsx b/ui/components/providers/workflow/forms/test-connection-form.tsx
index 6f21f146549..41e5ebed668 100644
--- a/ui/components/providers/workflow/forms/test-connection-form.tsx
+++ b/ui/components/providers/workflow/forms/test-connection-form.tsx
@@ -29,7 +29,7 @@ export const TestConnectionForm = ({
searchParams,
providerData,
}: {
- searchParams: { type: string; id: string };
+ searchParams: { type: string; id: string; updated: string };
providerData: {
data: {
id: string;
@@ -75,6 +75,7 @@ export const TestConnectionForm = ({
});
const isLoading = form.formState.isSubmitting;
+ const isUpdated = searchParams?.updated === "true";
const onSubmitClient = async (values: FormValues) => {
const formData = new FormData();
@@ -124,9 +125,19 @@ export const TestConnectionForm = ({
message: data.error,
});
} else {
- router.push(
- `/providers/launch-scan?type=${providerType}&id=${providerId}`,
- );
+ const urlParams = new URLSearchParams(window.location.search);
+ const isUpdated = urlParams.get("updated") === "true";
+
+ if (!isUpdated) {
+ router.push(
+ `/providers/launch-scan?type=${providerType}&id=${providerId}`,
+ );
+ } else {
+ setConnectionStatus({
+ connected: true,
+ error: null,
+ });
+ }
}
} catch (error) {
form.setError("providerId", {
@@ -254,7 +265,7 @@ export const TestConnectionForm = ({
) : connectionStatus?.error ? (
router.back() : onResetCredentials}
type="button"
ariaLabel={"Save"}
className="w-1/2"
@@ -268,12 +279,21 @@ export const TestConnectionForm = ({
{isResettingCredentials ? (
<>Loading>
) : (
- Reset credentials
+
+ {isUpdated ? "Update credentials" : "Reset credentials"}
+
)}
) : (
router.push("/providers")
+ : undefined
+ }
ariaLabel={"Save"}
className="w-1/2"
variant="solid"
@@ -282,7 +302,15 @@ export const TestConnectionForm = ({
isLoading={isLoading}
endContent={!isLoading && }
>
- {isLoading ? <>Loading> : Launch}
+ {isLoading ? (
+ <>Loading>
+ ) : (
+
+ {isUpdated && connectionStatus?.connected
+ ? "Go to providers"
+ : "Launch"}
+
+ )}
)}
diff --git a/ui/components/providers/workflow/forms/update-via-credentials-form.tsx b/ui/components/providers/workflow/forms/update-via-credentials-form.tsx
new file mode 100644
index 00000000000..c3279655913
--- /dev/null
+++ b/ui/components/providers/workflow/forms/update-via-credentials-form.tsx
@@ -0,0 +1,243 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { Control, useForm } from "react-hook-form";
+import * as z from "zod";
+
+import { updateCredentialsProvider } from "@/actions/providers/providers";
+import { useToast } from "@/components/ui";
+import { CustomButton } from "@/components/ui/custom";
+import { getProviderLogo } from "@/components/ui/entities";
+import { getProviderName } from "@/components/ui/entities";
+import { ProviderType } from "@/components/ui/entities";
+import { Form } from "@/components/ui/form";
+import {
+ addCredentialsFormSchema,
+ ApiError,
+ AWSCredentials,
+ AzureCredentials,
+ GCPCredentials,
+ KubernetesCredentials,
+} from "@/types";
+
+import { AWScredentialsForm } from "./via-credentials/aws-credentials-form";
+import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
+import { GCPcredentialsForm } from "./via-credentials/gcp-credentials-form";
+import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
+
+type CredentialsFormSchema = z.infer<
+ ReturnType
+>;
+
+// Add this type intersection to include all fields
+type FormType = CredentialsFormSchema &
+ AWSCredentials &
+ AzureCredentials &
+ GCPCredentials &
+ KubernetesCredentials;
+
+export const UpdateViaCredentialsForm = ({
+ searchParams,
+}: {
+ searchParams: { type: string; id: string; secretId?: string };
+}) => {
+ const router = useRouter();
+ const { toast } = useToast();
+
+ const searchParamsObj = useSearchParams();
+
+ // Handler for back button
+ const handleBackStep = () => {
+ const currentParams = new URLSearchParams(window.location.search);
+ currentParams.delete("via");
+ router.push(`?${currentParams.toString()}`);
+ };
+
+ const providerType = searchParams.type;
+ const providerId = searchParams.id;
+ const providerSecretId = searchParams.secretId || "";
+ const formSchema = addCredentialsFormSchema(providerType);
+
+ const form = useForm({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ providerId,
+ providerType,
+ ...(providerType === "aws"
+ ? {
+ aws_access_key_id: "",
+ aws_secret_access_key: "",
+ aws_session_token: "",
+ }
+ : providerType === "azure"
+ ? {
+ client_id: "",
+ client_secret: "",
+ tenant_id: "",
+ }
+ : providerType === "gcp"
+ ? {
+ client_id: "",
+ client_secret: "",
+ refresh_token: "",
+ }
+ : providerType === "kubernetes"
+ ? {
+ kubeconfig_content: "",
+ }
+ : {}),
+ },
+ });
+
+ const isLoading = form.formState.isSubmitting;
+
+ const onSubmitClient = async (values: FormType) => {
+ const formData = new FormData();
+
+ Object.entries(values).forEach(
+ ([key, value]) => value !== undefined && formData.append(key, value),
+ );
+
+ const data = await updateCredentialsProvider(providerSecretId, formData);
+
+ if (data?.errors && data.errors.length > 0) {
+ data.errors.forEach((error: ApiError) => {
+ const errorMessage = error.detail;
+ switch (error.source.pointer) {
+ case "/data/attributes/secret/aws_access_key_id":
+ form.setError("aws_access_key_id", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/aws_secret_access_key":
+ form.setError("aws_secret_access_key", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/aws_session_token":
+ form.setError("aws_session_token", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/client_id":
+ form.setError("client_id", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/client_secret":
+ form.setError("client_secret", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/tenant_id":
+ form.setError("tenant_id", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/secret/kubeconfig_content":
+ form.setError("kubeconfig_content", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ case "/data/attributes/name":
+ form.setError("secretName", {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+ default:
+ toast({
+ variant: "destructive",
+ title: "Oops! Something went wrong",
+ description: errorMessage,
+ });
+ }
+ });
+ } else {
+ router.push(
+ `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`,
+ );
+ }
+ };
+
+ return (
+
+
+ );
+};
diff --git a/ui/components/providers/workflow/forms/update-via-role-form.tsx b/ui/components/providers/workflow/forms/update-via-role-form.tsx
new file mode 100644
index 00000000000..ee57dc0759d
--- /dev/null
+++ b/ui/components/providers/workflow/forms/update-via-role-form.tsx
@@ -0,0 +1,149 @@
+"use client";
+
+import { zodResolver } from "@hookform/resolvers/zod";
+import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { Control, useForm } from "react-hook-form";
+import * as z from "zod";
+
+import { updateCredentialsProvider } from "@/actions/providers/providers";
+import { useToast } from "@/components/ui";
+import { CustomButton } from "@/components/ui/custom";
+import { Form } from "@/components/ui/form";
+import {
+ addCredentialsRoleFormSchema,
+ ApiError,
+ AWSCredentialsRole,
+} from "@/types";
+
+import { AWSCredentialsRoleForm } from "./via-role/aws-role-form";
+
+export const UpdateViaRoleForm = ({
+ searchParams,
+}: {
+ searchParams: { type: string; id: string; secretId?: string };
+}) => {
+ const router = useRouter();
+ const { toast } = useToast();
+
+ const searchParamsObj = useSearchParams();
+
+ // Handler for back button
+ const handleBackStep = () => {
+ const currentParams = new URLSearchParams(window.location.search);
+ currentParams.delete("via");
+ router.push(`?${currentParams.toString()}`);
+ };
+
+ const providerType = searchParams.type;
+ const providerId = searchParams.id;
+ const providerSecretId = searchParams.secretId || "";
+
+ const formSchema = addCredentialsRoleFormSchema(providerType);
+ type FormSchemaType = z.infer;
+
+ const form = useForm({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ providerId,
+ providerType,
+ ...(providerType === "aws"
+ ? {
+ role_arn: "",
+ aws_access_key_id: "",
+ aws_secret_access_key: "",
+ aws_session_token: "",
+ session_duration: 3600,
+ external_id: "",
+ role_session_name: "",
+ }
+ : {}),
+ },
+ });
+
+ const isLoading = form.formState.isSubmitting;
+
+ const onSubmitClient = async (values: FormSchemaType) => {
+ const formData = new FormData();
+
+ Object.entries(values).forEach(
+ ([key, value]) =>
+ value !== undefined && formData.append(key, String(value)),
+ );
+
+ const data = await updateCredentialsProvider(providerSecretId, formData);
+
+ if (data?.errors && data.errors.length > 0) {
+ data.errors.forEach((error: ApiError) => {
+ const errorMessage = error.detail;
+ switch (error.source.pointer) {
+ case "/data/attributes/secret/role_arn":
+ form.setError("role_arn" as keyof FormSchemaType, {
+ type: "server",
+ message: errorMessage,
+ });
+ break;
+
+ default:
+ toast({
+ variant: "destructive",
+ title: "Oops! Something went wrong",
+ description: errorMessage,
+ });
+ }
+ });
+ } else {
+ router.push(
+ `/providers/test-connection?type=${providerType}&id=${providerId}&updated=true`,
+ );
+ }
+ };
+
+ return (
+
+
+ );
+};
diff --git a/ui/components/providers/workflow/forms/via-credentials-form.tsx b/ui/components/providers/workflow/forms/via-credentials-form.tsx
index 3ecb11da415..6c0bd0fe371 100644
--- a/ui/components/providers/workflow/forms/via-credentials-form.tsx
+++ b/ui/components/providers/workflow/forms/via-credentials-form.tsx
@@ -1,8 +1,8 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { ChevronRightIcon } from "lucide-react";
-import { useRouter } from "next/navigation";
+import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
+import { useRouter, useSearchParams } from "next/navigation";
import { Control, useForm } from "react-hook-form";
import * as z from "zod";
@@ -46,6 +46,15 @@ export const ViaCredentialsForm = ({
const router = useRouter();
const { toast } = useToast();
+ const searchParamsObj = useSearchParams();
+
+ // Handler for back button
+ const handleBackStep = () => {
+ const currentParams = new URLSearchParams(window.location.search);
+ currentParams.delete("via");
+ router.push(`?${currentParams.toString()}`);
+ };
+
const providerType = searchParams.type;
const providerId = searchParams.id;
const formSchema = addCredentialsFormSchema(providerType);
@@ -199,6 +208,21 @@ export const ViaCredentialsForm = ({
)}
+ {searchParamsObj.get("via") === "credentials" && (
+
}
+ isDisabled={isLoading}
+ >
+
Back
+
+ )}
{
+ const currentParams = new URLSearchParams(window.location.search);
+ currentParams.delete("via");
+ router.push(`?${currentParams.toString()}`);
+ };
+
const providerType = searchParams.type;
const providerId = searchParams.id;
@@ -54,7 +63,6 @@ export const ViaRoleForm = ({
const isLoading = form.formState.isSubmitting;
const onSubmitClient = async (values: FormSchemaType) => {
- console.log("via ROLE form", values);
const formData = new FormData();
Object.entries(values).forEach(
@@ -106,6 +114,21 @@ export const ViaRoleForm = ({
)}
+ {searchParamsObj.get("via") === "role" && (
+ }
+ isDisabled={isLoading}
+ >
+ Back
+
+ )}
}
+ endContent={!isLoading && }
>
- {isLoading ? <>Loading> : Save}
+ {isLoading ? <>Loading> : Next}
diff --git a/ui/components/ui/sidebar/sidebar-wrap.tsx b/ui/components/ui/sidebar/sidebar-wrap.tsx
index 4f3fba89df1..0f8900854bf 100644
--- a/ui/components/ui/sidebar/sidebar-wrap.tsx
+++ b/ui/components/ui/sidebar/sidebar-wrap.tsx
@@ -163,7 +163,7 @@ export const SidebarWrap = () => {
scheduleDate: z.string(),
});
+export const awsCredentialsTypeSchema = z.object({
+ awsCredentialsType: z.string().min(1, {
+ message: "Please select the type of credentials you want to use",
+ }),
+});
+
export const addProviderFormSchema = z
.object({
providerType: z.enum(["aws", "azure", "gcp", "kubernetes"], {
@@ -43,9 +49,6 @@ export const addProviderFormSchema = z
providerType: z.literal("aws"),
providerAlias: z.string(),
providerUid: z.string(),
- awsCredentialsType: z.string().min(1, {
- message: "Please select the type of credentials you want to use",
- }),
}),
z.object({
providerType: z.literal("azure"),