diff --git a/src/components/Connections/ConnectionForm.tsx b/src/components/Connections/ConnectionForm.tsx index b5e5db95a..ddce47f9d 100644 --- a/src/components/Connections/ConnectionForm.tsx +++ b/src/components/Connections/ConnectionForm.tsx @@ -1,102 +1,52 @@ import clsx from "clsx"; import { Form, Formik } from "formik"; -import FormikTextInput from "../Forms/Formik/FormikTextInput"; -import FormikCheckbox from "../Forms/Formik/FormikCheckbox"; -import { Modal } from "../Modal"; -import { useEffect, useState } from "react"; -import { - ConnectionType, - ConnectionValueType, - Field, - connectionTypes -} from "./connectionTypes"; -import { FormikEnvVarSource } from "../Forms/Formik/FormikEnvVarSource"; -import { Icon } from "../Icon"; -import React from "react"; +import { mapValues, method } from "lodash"; +import { useMemo } from "react"; import { FaTrash } from "react-icons/fa"; import { Button } from "../Button"; -import { mapValues, method } from "lodash"; +import { Connection } from "./ConnectionFormModal"; +import RenderConnectionFormFields from "./RenderConnectionFormFields"; +import { ConnectionType, connectionTypes } from "./connectionTypes"; -export type Connection = { - altID?: string; - authMethod?: string; - certificate?: string; - channel?: string; - checkIntegrity?: boolean; - contentType?: string; - db?: string; - domain?: string; - email?: string; - encryptionMethod?: string; - from?: string; - fromName?: string; - group?: string; - groupOwner?: string; - host?: string; - id?: string; - insecure_tls?: boolean; - key?: string; - maxAge?: number; - name: string; - password?: string; - path?: string; - port?: string | number; - profile?: string; - region?: string; - requestMethod?: string; - scheme?: string; - searchPath?: string; - sharename?: string; - targets?: string; - tenant?: string; - titleKey?: string; - topic?: string; - type?: ConnectionValueType; - url?: string; - user?: string; - username?: string; - webhook?: string; - workstation?: string; - properties?: Record; -}; - -type ConnectionFormProps = React.HTMLProps & { - isOpen: boolean; - setIsOpen: (val: boolean) => void; - onConnectionSubmit: (data: Connection) => Promise; - onConnectionDelete: (data: Connection) => Promise; +interface ConnectionFormProps { + connectionType: ConnectionType; + onConnectionSubmit?: (data: Connection) => void; + onConnectionDelete?: (data: Connection) => void; + className?: string; formValue?: Connection; -}; + handleBack?: () => void; +} -export default function ConnectionForm({ - className, - isOpen, - setIsOpen, +export function ConnectionForm({ + connectionType, + formValue, onConnectionSubmit, onConnectionDelete, - formValue, + className, + handleBack = () => {}, ...props }: ConnectionFormProps) { - const [connectionType, setConnectionType] = useState(); - const [formInitialValue, setFormInitialValue] = useState(); + const handleSubmit = (value: any) => { + onConnectionSubmit?.({ + ...convertData(value), + type: connectionType.value + }); + }; - useEffect(() => { - let connection = connectionTypes.find( - (item) => item.title === formValue?.type + const formInitialValue = useMemo(() => { + const connection = connectionTypes.find( + (item) => item.value === connectionType.value ); if (connection) { - setConnectionType(connection); - setFormInitialValue( - connection.convertToFormSpecificValue - ? connection.convertToFormSpecificValue(formValue as any) - : formValue - ); - return; + const res = connection.convertToFormSpecificValue + ? connection.convertToFormSpecificValue(formValue as any) + : formValue; + return { + ...res, + namespace: res?.namespace ?? "default" + }; } - setTimeout(() => { - setConnectionType(connection); - }, 1000); - }, [isOpen, formValue]); + }, [connectionType.value, formValue]); const convertData = (data: Connection) => { if (connectionType?.preSubmitConverter) { @@ -118,192 +68,75 @@ export default function ConnectionForm({ } as Connection; }; - const getFieldView = (field: Field) => { - const type = field.type ?? "input"; - switch (type) { - case "input": - return ( - - ); - case "numberInput": - return ( - - ); - case "checkbox": - return ( - - ); - case "EnvVarSource": - return ( - - ); - default: - return null; - } + const handleDelete = () => { + onConnectionDelete?.(formValue!); }; - const getFormView = (connectionType: ConnectionType) => { - return ( - { - onConnectionSubmit?.({ - ...convertData(value), - type: connectionType.value - }); - }} - > -
-
-
-
- {connectionType.fields.map((field, index) => { - return ( - - {getFieldView(field)} - - ); - })} -
-
-
-
- {Boolean(formValue?.id) && ( - - )} -
-
- ) : ( -
Select Connection Type
- ) - } - onClose={() => { - setIsOpen(false); - }} - open={isOpen} - bodyClass="flex flex-col w-full flex-1 h-full overflow-y-auto" - > - {connectionType && getFormView(connectionType)} - {!connectionType && getConnectionListingView()} - -
+ Back + + )} +
+
+ + +
); } + +export default ConnectionForm; diff --git a/src/components/Connections/ConnectionFormModal.tsx b/src/components/Connections/ConnectionFormModal.tsx new file mode 100644 index 000000000..fb1478663 --- /dev/null +++ b/src/components/Connections/ConnectionFormModal.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from "react"; +import { Icon } from "../Icon"; +import { Modal } from "../Modal"; +import ConnectionForm from "./ConnectionForm"; +import { + ConnectionType, + ConnectionValueType, + connectionTypes +} from "./connectionTypes"; +import ConnectionListView from "./ConnectionListView"; + +export type Connection = { + altID?: string; + authMethod?: string; + certificate?: string; + channel?: string; + checkIntegrity?: boolean; + contentType?: string; + db?: string; + domain?: string; + email?: string; + encryptionMethod?: string; + from?: string; + fromName?: string; + group?: string; + groupOwner?: string; + host?: string; + id?: string; + insecure_tls?: boolean; + key?: string; + maxAge?: number; + name: string; + password?: string; + path?: string; + port?: string | number; + profile?: string; + region?: string; + requestMethod?: string; + scheme?: string; + searchPath?: string; + sharename?: string; + targets?: string; + tenant?: string; + titleKey?: string; + topic?: string; + type?: ConnectionValueType; + url?: string; + user?: string; + username?: string; + webhook?: string; + workstation?: string; + properties?: Record; + ref?: string; + namespace?: string; +}; + +type ConnectionFormProps = React.HTMLProps & { + isOpen: boolean; + setIsOpen: (val: boolean) => void; + onConnectionSubmit: (data: Connection) => Promise; + onConnectionDelete: (data: Connection) => Promise; + formValue?: Connection; +}; + +export default function ConnectionFormModal({ + className, + isOpen, + setIsOpen, + onConnectionSubmit, + onConnectionDelete, + formValue +}: ConnectionFormProps) { + const [connectionType, setConnectionType] = useState< + ConnectionType | undefined + >(() => connectionTypes.find((item) => item.title === formValue?.type)); + + useEffect(() => { + let connection = connectionTypes.find( + (item) => item.value === formValue?.type + ); + setConnectionType(connection); + }, [isOpen, formValue]); + + const type = + connectionTypes.find((item) => item.value === formValue?.type) ?? + connectionType; + + return ( +
+ + {typeof connectionType?.icon === "string" ? ( + + ) : ( + connectionType.icon + )} +
+ {connectionType.title} Connection Details +
+
+ ) : ( +
Select Connection Type
+ ) + } + onClose={() => { + setIsOpen(false); + }} + open={isOpen} + bodyClass="flex flex-col w-full flex-1 h-full overflow-y-auto" + > + {type ? ( + setConnectionType(undefined)} + connectionType={type} + onConnectionSubmit={onConnectionSubmit} + onConnectionDelete={onConnectionDelete} + formValue={formValue} + className={className} + /> + ) : ( + + )} + + + ); +} diff --git a/src/components/Connections/ConnectionListView.tsx b/src/components/Connections/ConnectionListView.tsx new file mode 100644 index 000000000..bd4f43427 --- /dev/null +++ b/src/components/Connections/ConnectionListView.tsx @@ -0,0 +1,33 @@ +import { Icon } from "../Icon"; +import { ConnectionType, connectionTypes } from "./connectionTypes"; + +type Props = { + setConnectionType: (connectionType: ConnectionType) => void; +}; + +export default function ConnectionListView({ setConnectionType }: Props) { + return ( +
+ {connectionTypes.map((item) => { + return ( +
+
{ + setConnectionType(item); + }} + > + {typeof item.icon === "string" ? ( + + ) : ( + item.icon + )} +
{item.title}
+
+
+ ); + })} +
+ ); +} diff --git a/src/components/Connections/ConnectionsList.tsx b/src/components/Connections/ConnectionsList.tsx index 92d2fd5a6..db04b425f 100644 --- a/src/components/Connections/ConnectionsList.tsx +++ b/src/components/Connections/ConnectionsList.tsx @@ -2,7 +2,7 @@ import { CellContext, ColumnDef } from "@tanstack/table-core"; import clsx from "clsx"; import { DataTable } from "../DataTable"; import { Avatar } from "../Avatar"; -import { Connection } from "./ConnectionForm"; +import { Connection } from "./ConnectionFormModal"; import { Icon } from "../Icon"; import { DateCell } from "../../ui/table"; @@ -31,6 +31,10 @@ const columns: ColumnDef[] = [ accessorKey: "name", cell: NameCell }, + { + header: "Namespace", + accessorKey: "namespace" + }, { header: "Type", accessorKey: "type" diff --git a/src/components/Connections/FormikConnectionOptionsSwitchField.tsx b/src/components/Connections/FormikConnectionOptionsSwitchField.tsx new file mode 100644 index 000000000..9260ba702 --- /dev/null +++ b/src/components/Connections/FormikConnectionOptionsSwitchField.tsx @@ -0,0 +1,62 @@ +import { useFormikContext } from "formik"; +import { get } from "lodash"; +import { useState } from "react"; +import { Switch } from "../Switch"; +import RenderConnectionFormFields from "./RenderConnectionFormFields"; +import { ConnectionFormFields } from "./connectionTypes"; + +type Props = { + field: ConnectionFormFields; +}; + +export default function FormikConnectionOptionsSwitchField({ field }: Props) { + const { setFieldValue, values } = useFormikContext>(); + + const [selectedGroup, setSelectedGroup] = useState(() => { + // find the first field that has a value + const firstField = field.options?.find((option) => { + return option.fields?.find((field) => get(values, field.key)); + }); + return firstField?.key ?? "None"; + }); + + if (!field.options) { + return null; + } + + const selectedField = field.options.find( + (option) => option.key === selectedGroup + ); + + return ( +
+ +
+ option.label)]} + defaultValue="None" + value={ + field.options?.find((option) => option.key === selectedGroup)?.label + } + onChange={(v) => { + // reset all other fields that are not selected + field.options?.forEach((option) => { + if (option.key === v) { + return; + } + setFieldValue(option.key, undefined); + }); + setSelectedGroup( + field.options?.find((option) => option.label === v)?.key ?? "None" + ); + }} + /> +
+
+ {selectedField?.fields?.map((field) => ( + + ))} +
+
+ ); +} diff --git a/src/components/Connections/RenderConnectionFormFields.tsx b/src/components/Connections/RenderConnectionFormFields.tsx new file mode 100644 index 000000000..a3f4cac24 --- /dev/null +++ b/src/components/Connections/RenderConnectionFormFields.tsx @@ -0,0 +1,72 @@ +import FormikCheckbox from "../Forms/Formik/FormikCheckbox"; +import { FormikCompactEnvVarSource } from "../Forms/Formik/FormikCompactEnvVarSource"; +import { FormikEnvVarSource } from "../Forms/Formik/FormikEnvVarSource"; +import FormikTextInput from "../Forms/Formik/FormikTextInput"; +import FormikConnectionOptionsSwitchField from "./FormikConnectionOptionsSwitchField"; +import { ConnectionFormFields } from "./connectionTypes"; + +interface FieldViewProps { + field: ConnectionFormFields; +} + +export default function RenderConnectionFormFields({ field }: FieldViewProps) { + const type = field.type ?? "input"; + switch (type) { + case "input": + return ( + + ); + case "numberInput": + return ( + + ); + case "checkbox": + return ( + + ); + case "EnvVarSource": + return ( + + ); + case "switch": + return ; + + case "authentication": + return ( + + ); + default: + return null; + } +} diff --git a/src/components/Connections/__tests__/ConnectionForm.unit.test.tsx b/src/components/Connections/__tests__/ConnectionForm.unit.test.tsx new file mode 100644 index 000000000..d48dc1465 --- /dev/null +++ b/src/components/Connections/__tests__/ConnectionForm.unit.test.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import ConnectionForm from "./../ConnectionForm"; +import { + ConnectionType, + ConnectionValueType, + connectionTypes +} from "../connectionTypes"; + +describe("ConnectionForm", () => { + const connectionType: ConnectionType = connectionTypes.find( + (type) => type.value === ConnectionValueType.Git + )!; + + const formInitialValue = { + name: "Test Connection", + url: "https://test.com", + certificate: "test", + ref: "main" + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("renders the form fields", () => { + render( + + ); + expect(screen.getByLabelText("Name")).toHaveValue("Test Connection"); + expect(screen.getByLabelText("URL")).toHaveValue("https://test.com"); + }); + + it("calls onConnectionSubmit when the form is submitted", async () => { + const onConnectionSubmit = jest.fn(); + render( + + ); + fireEvent.click( + screen.getByRole("button", { + name: /Save/i + }) + ); + + await waitFor(() => { + expect(onConnectionSubmit).toHaveBeenCalledWith({ + certificate: "test", + name: "Test Connection", + password: "", + properties: { + ref: "ref" + }, + type: "git", + url: "https://test.com", + username: "", + namespace: "default" + }); + }); + }); + + it("calls onConnectionDelete when the delete button is clicked", () => { + const onConnectionDelete = jest.fn(); + render( + + ); + fireEvent.click(screen.getByText("Delete")); + expect(onConnectionDelete).toHaveBeenCalledWith({ + name: "Test Connection", + url: "https://test.com", + ref: "main", + certificate: "test", + id: "123" + }); + }); + + it("calls handleBack when the back button is clicked", () => { + const handleBack = jest.fn(); + render( + + ); + fireEvent.click(screen.getByText("Back")); + expect(handleBack).toHaveBeenCalled(); + }); +}); diff --git a/src/components/Connections/__tests__/ConnectionFormModal.unit.test.tsx b/src/components/Connections/__tests__/ConnectionFormModal.unit.test.tsx new file mode 100644 index 000000000..9cae96160 --- /dev/null +++ b/src/components/Connections/__tests__/ConnectionFormModal.unit.test.tsx @@ -0,0 +1,94 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import ConnectionFormModal from "../ConnectionFormModal"; +import { ConnectionValueType } from "../connectionTypes"; + +global.ResizeObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn() +})); + +describe("ConnectionForm", () => { + const formInitialValue = { + type: ConnectionValueType.Git, + name: "Test Connection", + url: "https://test.com", + certificate: "test", + ref: "main" + }; + + const onConnectionSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("renders form when provided with initial value", async () => { + render( + {}} + onConnectionDelete={async (data) => {}} + onConnectionSubmit={onConnectionSubmit} + /> + ); + + await waitFor(() => screen.findByRole("button", { name: /Save/i })); + + expect(screen.getByLabelText("Name")).toHaveValue("Test Connection"); + expect(screen.getByLabelText("URL")).toHaveValue("https://test.com"); + + fireEvent.click( + screen.getByRole("button", { + name: /Save/i + }) + ); + + await waitFor(() => { + expect(onConnectionSubmit).toHaveBeenCalledWith({ + certificate: "test", + name: "Test Connection", + password: "", + properties: { + ref: "ref" + }, + type: "git", + url: "https://test.com", + username: "", + namespace: "default" + }); + }); + }); + + it("renders list of connection types", async () => { + render( + {}} + onConnectionSubmit={async (data) => {}} + onConnectionDelete={async (data) => {}} + /> + ); + + expect(await screen.findByText("Git")).toBeInTheDocument(); + expect(screen.getByText("GitHub")).toBeInTheDocument(); + }); + + it("renders form when connection type is selected", async () => { + render( + {}} + onConnectionSubmit={async (data) => {}} + onConnectionDelete={async (data) => {}} + /> + ); + + fireEvent.click(await screen.findByText("Git")); + + expect(await screen.findByLabelText("URL")).toBeInTheDocument(); + }); +}); diff --git a/src/components/Connections/connectionTypes.tsx b/src/components/Connections/connectionTypes.tsx index b7d6ec58d..b01eaf96f 100644 --- a/src/components/Connections/connectionTypes.tsx +++ b/src/components/Connections/connectionTypes.tsx @@ -1,14 +1,16 @@ import { FaWindows } from "react-icons/fa"; import { DiGoogleCloudPlatform } from "react-icons/di"; import { stringSortHelper } from "../../utils/common"; -import { Connection } from "./ConnectionForm"; +import { Connection } from "./ConnectionFormModal"; -const fieldTypes = { - checkbox: "checkbox", - input: "input", - numberInput: "numberInput", - EnvVarSource: "EnvVarSource" -}; +const enum ConnectionsFieldTypes { + checkbox = "checkbox", + input = "input", + numberInput = "numberInput", + EnvVarSource = "EnvVarSource", + switch = "switch", + Authentication = "authentication" +} type Variant = "small" | "large"; @@ -17,14 +19,20 @@ const variants: { [key: string]: Variant } = { large: "large" }; -export type Field = { +export type ConnectionFormFields = { label: string; key: string; - type: string; + type: ConnectionsFieldTypes; variant?: Variant; required?: boolean; hint?: string; default?: boolean | number | string; + hideLabel?: boolean; + options?: { + label: string; + key: string; + fields: Omit[]; + }[]; }; export const enum ConnectionValueType { @@ -74,7 +82,7 @@ export type ConnectionType = { title: string; value: ConnectionValueType; icon?: React.ReactNode | string | null; - fields: Field[]; + fields: ConnectionFormFields[]; convertToFormSpecificValue?: (data: Record) => Connection; preSubmitConverter?: (data: Record) => object; hide?: boolean; @@ -83,34 +91,45 @@ export type ConnectionType = { forNotification?: boolean; }; +export const commonConnectionFormFields: ConnectionFormFields[] = [ + { + label: "Name", + key: "name", + type: ConnectionsFieldTypes.input, + required: true + }, + { + label: "Namespace", + key: "namespace", + type: ConnectionsFieldTypes.input, + default: "default", + required: true + } +]; + export const connectionTypes: ConnectionType[] = [ { title: "Postgres", icon: "postgres", value: ConnectionValueType.Postgres, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ] @@ -120,28 +139,23 @@ export const connectionTypes: ConnectionType[] = [ icon: "mysql", value: ConnectionValueType.MySQL, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ] @@ -151,28 +165,23 @@ export const connectionTypes: ConnectionType[] = [ icon: "sqlserver", value: ConnectionValueType.SQLServer, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ] @@ -182,34 +191,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "http", value: ConnectionValueType.HTTP, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ] }, @@ -218,34 +222,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "prometheus", value: ConnectionValueType.Prometheus, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ] }, @@ -254,34 +253,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "elasticsearch", value: ConnectionValueType.ElasticSearch, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ] }, @@ -290,34 +284,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "mongo", value: ConnectionValueType.Mongo, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ] }, @@ -326,34 +315,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "ldap", value: ConnectionValueType.LDAP, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ] }, @@ -362,46 +346,41 @@ export const connectionTypes: ConnectionType[] = [ icon: "redis", value: ConnectionValueType.Redis, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Database", key: "db", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - db: data.properties?.db + db: data?.properties?.db } as Connection; }, preSubmitConverter: (data: Record) => { @@ -422,35 +401,30 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.Windows, icon: , fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Domain", key: "domain", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - domain: data.properties?.domain + domain: data?.properties?.domain } as Connection; }, preSubmitConverter: (data: Record) => { @@ -470,22 +444,17 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.GCP, icon: , fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Endpoint", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Certificate", key: "certificate", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, variant: variants.large, required: true } @@ -496,34 +465,29 @@ export const connectionTypes: ConnectionType[] = [ icon: "sftp", value: ConnectionValueType.SFTP, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Username", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Port", key: "port", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false } ], @@ -550,48 +514,43 @@ export const connectionTypes: ConnectionType[] = [ icon: "aws", value: ConnectionValueType.AWS, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Region", key: "region", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Profile", key: "profile", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Access Key", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Secret Key", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - region: data.properties?.region, - profile: data.properties?.profile, - insecure_tls: data.properties?.insecureTLS === "true" + region: data?.properties?.region, + profile: data?.properties?.profile, + insecure_tls: data?.properties?.insecureTLS === "true" } as Connection; }, preSubmitConverter: (data: Record) => { @@ -612,16 +571,11 @@ export const connectionTypes: ConnectionType[] = [ icon: "kubernetes", value: ConnectionValueType.Kubernetes, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Certificate", key: "certificate", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, variant: variants.large, required: false } @@ -632,22 +586,17 @@ export const connectionTypes: ConnectionType[] = [ icon: "azure-devops", value: ConnectionValueType.AzureDevops, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Organization", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Personal Access Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ] @@ -657,35 +606,30 @@ export const connectionTypes: ConnectionType[] = [ icon: "azure", value: ConnectionValueType.Azure, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Client ID", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Client Secret", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Tenant ID", key: "tenant", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - tenant: data.properties?.tenant + tenant: data?.properties?.tenant } as Connection; }, preSubmitConverter: (data: Record) => { @@ -704,16 +648,11 @@ export const connectionTypes: ConnectionType[] = [ icon: "github", value: ConnectionValueType.Github, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Personal Access Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ] @@ -723,40 +662,35 @@ export const connectionTypes: ConnectionType[] = [ icon: "restic", value: ConnectionValueType.Restic, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Repository URL", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "AWS Connection Name", key: "awsConnectionName", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Access Key", key: "accessKey", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Secret Key", key: "secretKey", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ], @@ -782,53 +716,48 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.SMB, hide: true, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Password", key: "password", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Workstation", key: "workstation", - type: fieldTypes.input + type: ConnectionsFieldTypes.input }, { label: "Share name", key: "sharename", - type: fieldTypes.input + type: ConnectionsFieldTypes.input }, { label: "Search path", key: "searchPath", - type: fieldTypes.input + type: ConnectionsFieldTypes.input }, { label: "Port", key: "port", - type: fieldTypes.numberInput, + type: ConnectionsFieldTypes.numberInput, default: 445 } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - workstation: data.properties?.workstation, - sharename: data.properties?.sharename, - searchPath: data.properties?.searchPath, - port: data.properties?.port + workstation: data?.properties?.workstation, + sharename: data?.properties?.sharename, + searchPath: data?.properties?.searchPath, + port: data?.properties?.port } as Connection; }, preSubmitConverter: (data: Record) => { @@ -851,28 +780,23 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.JMeter, hide: true, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Host", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Port", key: "port", - type: fieldTypes.numberInput + type: ConnectionsFieldTypes.numberInput } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - port: data.properties?.port + port: data?.properties?.port } as Connection; }, preSubmitConverter: (data: Record) => { @@ -890,35 +814,30 @@ export const connectionTypes: ConnectionType[] = [ icon: "dynatrace", value: ConnectionValueType.Dynatrace, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Host", key: "url", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "API Key", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Scheme", key: "scheme", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - scheme: data.properties?.scheme + scheme: data?.properties?.scheme } as Connection; }, preSubmitConverter: (data: Record) => { @@ -938,22 +857,17 @@ export const connectionTypes: ConnectionType[] = [ icon: "discord", value: ConnectionValueType.Discord, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Webhook ID", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ], @@ -972,54 +886,49 @@ export const connectionTypes: ConnectionType[] = [ icon: "email", value: ConnectionValueType.Email, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Password", key: "password", hint: "SMTP server password or hash (for OAuth2)", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "From Address", key: "from", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "From Name", key: "fromName", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Port", key: "port", - type: fieldTypes.numberInput, + type: ConnectionsFieldTypes.numberInput, default: 25, required: true }, { label: "Encryption method", key: "encryptionMethod", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, hint: "None, ExplicitTLS, ImplicitTLS, Auto (default)", default: "Auto", required: true @@ -1027,7 +936,7 @@ export const connectionTypes: ConnectionType[] = [ { label: "SMTP authentication method", key: "authMethod", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, hint: "None, Plain, CRAMMD5, Unknown, OAuth2", default: "Unknown", required: true @@ -1035,18 +944,18 @@ export const connectionTypes: ConnectionType[] = [ { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - authMethod: data.properties?.authMethod, - encryptionMethod: data.properties?.encryptionMethod, - from: data.properties?.from, - fromName: data.properties?.fromName, - host: data.properties?.host, - port: data.properties?.port + authMethod: data?.properties?.authMethod, + encryptionMethod: data?.properties?.encryptionMethod, + from: data?.properties?.from, + fromName: data?.properties?.fromName, + host: data?.properties?.host, + port: data?.properties?.port } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1073,35 +982,30 @@ export const connectionTypes: ConnectionType[] = [ icon: "google-chat", value: ConnectionValueType.GoogleChat, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Key", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Webhook Name", key: "webhook", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - webhook: data.properties?.webhook + webhook: data?.properties?.webhook } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1122,16 +1026,11 @@ export const connectionTypes: ConnectionType[] = [ icon: "ifttt", value: ConnectionValueType.IFTTT, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Webhook ID", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], @@ -1149,46 +1048,41 @@ export const connectionTypes: ConnectionType[] = [ icon: "mattermost", value: ConnectionValueType.Mattermost, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "username", hint: "Override webhook user", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Host", key: "host", hint: "Mattermost server host (host:port)", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Token", key: "password", hint: "Webhook token", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Channel", key: "channel", hint: "Override webhook channel", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host, - channel: data.properties?.channel + host: data?.properties?.host, + channel: data?.properties?.channel } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1210,43 +1104,38 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.Matrix, icon: "matrix", fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "User", key: "username", hint: "Username or empty when using access token", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Password", key: "password", hint: "Password or access token", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host, - channel: data.properties?.channel + host: data?.properties?.host, + channel: data?.properties?.channel } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1268,22 +1157,17 @@ export const connectionTypes: ConnectionType[] = [ value: ConnectionValueType.Ntfy, icon: "ntfy", fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Password", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: false }, { @@ -1291,27 +1175,27 @@ export const connectionTypes: ConnectionType[] = [ key: "host", hint: "Server hostname and port", default: "ntfy.sh", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Topic", key: "topic", hint: "Target topic name", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host, - topic: data.properties?.topic + host: data?.properties?.host, + topic: data?.properties?.topic } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1335,38 +1219,33 @@ export const connectionTypes: ConnectionType[] = [ icon: "opsgenie", value: ConnectionValueType.OpsGenie, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Host", key: "host", default: "api.opsgenie.com", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Port", key: "port", default: 443, - type: fieldTypes.numberInput, + type: ConnectionsFieldTypes.numberInput, required: true }, { label: "API Key", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host, - port: data.properties?.port + host: data?.properties?.host, + port: data?.properties?.port } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1387,29 +1266,24 @@ export const connectionTypes: ConnectionType[] = [ icon: "pushbullet", value: ConnectionValueType.Pushbullet, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Targets", key: "targets", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - targets: data.properties?.targets + targets: data?.properties?.targets } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1429,22 +1303,17 @@ export const connectionTypes: ConnectionType[] = [ icon: "pushover", value: ConnectionValueType.Pushover, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "User", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true } ], @@ -1463,56 +1332,51 @@ export const connectionTypes: ConnectionType[] = [ icon: "rocket", value: ConnectionValueType.Rocketchat, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Username", key: "user", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Port", key: "port", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Token A", key: "username", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Token B", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Channel", key: "channel", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host, - port: data.properties?.port, - user: data.properties?.user, - channel: data.properties?.channel + host: data?.properties?.host, + port: data?.properties?.port, + user: data?.properties?.user, + channel: data?.properties?.channel } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1536,36 +1400,31 @@ export const connectionTypes: ConnectionType[] = [ icon: "slack", value: ConnectionValueType.Slack, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Channel", key: "username", hint: "Channel to send messages to in Cxxxxxxxxxx format", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Bot Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Bot Name", key: "fromName", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - fromName: data.properties?.BotName + fromName: data?.properties?.BotName } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1586,40 +1445,35 @@ export const connectionTypes: ConnectionType[] = [ icon: "teams", value: ConnectionValueType.Teams, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Group", key: "group", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "Tenant", key: "tenant", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "AltID", key: "altID", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "GroupOwner", key: "groupOwner", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, default: "outlook.office.com", required: true } @@ -1627,11 +1481,11 @@ export const connectionTypes: ConnectionType[] = [ convertToFormSpecificValue: (data: Record) => { return { ...data, - group: data.properties?.group, - tenant: data.properties?.tenant, - altID: data.properties?.altID, - host: data.properties?.host, - groupOwner: data.properties?.groupOwner + group: data?.properties?.group, + tenant: data?.properties?.tenant, + altID: data?.properties?.altID, + host: data?.properties?.host, + groupOwner: data?.properties?.groupOwner } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1654,23 +1508,18 @@ export const connectionTypes: ConnectionType[] = [ icon: "telegram", value: ConnectionValueType.Telegram, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "Token", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Chats", hitns: "Chat IDs or Channel names (using @channel-name)", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], @@ -1689,36 +1538,31 @@ export const connectionTypes: ConnectionType[] = [ icon: "zulip", value: ConnectionValueType.ZulipChat, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "BotMail", key: "username", hint: "Bot e-mail address", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: false }, { label: "BotKey", key: "password", - type: fieldTypes.EnvVarSource, + type: ConnectionsFieldTypes.EnvVarSource, required: true }, { label: "Host", key: "host", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - host: data.properties?.host + host: data?.properties?.host } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1739,28 +1583,23 @@ export const connectionTypes: ConnectionType[] = [ icon: "http", value: ConnectionValueType.GenericWebhook, fields: [ - { - label: "Name", - key: "name", - type: fieldTypes.input, - required: true - }, + ...commonConnectionFormFields, { label: "URL", key: "username", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "ContentType", default: "application/json", key: "contentType", - type: fieldTypes.input + type: ConnectionsFieldTypes.input }, { label: "Request Method", key: "requestMethod", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, default: "POST", required: true }, @@ -1768,29 +1607,29 @@ export const connectionTypes: ConnectionType[] = [ label: "Message Key", default: "message", key: "key", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Title Key", default: "title", key: "titleKey", - type: fieldTypes.input, + type: ConnectionsFieldTypes.input, required: true }, { label: "Insecure TLS", key: "insecure_tls", - type: fieldTypes.checkbox + type: ConnectionsFieldTypes.checkbox } ], convertToFormSpecificValue: (data: Record) => { return { ...data, - contentType: data.properties?.contentType, - requestMethod: data.properties?.requestMethod, - key: data.properties?.key, - titleKey: data.properties?.titleKey + contentType: data?.properties?.contentType, + requestMethod: data?.properties?.requestMethod, + key: data?.properties?.key, + titleKey: data?.properties?.titleKey } as Connection; }, preSubmitConverter: (data: Record) => { @@ -1807,6 +1646,83 @@ export const connectionTypes: ConnectionType[] = [ } }; } + }, + { + title: "Git", + icon: "git", + value: ConnectionValueType.Git, + fields: [ + ...commonConnectionFormFields, + { + label: "URL", + key: "url", + type: ConnectionsFieldTypes.input, + required: true + }, + { + label: "Authentication", + key: "authentication", + type: ConnectionsFieldTypes.switch, + default: "password", + options: [ + { + label: "Basic", + key: "password", + fields: [ + { + label: "Username", + key: "username", + type: ConnectionsFieldTypes.Authentication + }, + { + label: "Password", + key: "password", + type: ConnectionsFieldTypes.Authentication + } + ] + }, + { + label: "SSH", + key: "certificate", + fields: [ + { + label: "SSH Key", + hideLabel: true, + key: "certificate", + type: ConnectionsFieldTypes.EnvVarSource, + variant: variants.large + } + ] + } + ] + }, + { + label: "Ref", + default: "main", + key: "ref", + type: ConnectionsFieldTypes.input, + required: true + } + ], + convertToFormSpecificValue: (data: Record) => { + return { + ...data, + ref: data?.properties?.ref ?? "ref" + } as Connection; + }, + preSubmitConverter: (data: Record) => { + return { + name: data.name, + url: data.url, + namespace: data.namespace, + password: data.password, + username: data.username, + certificate: data.certificate, + properties: { + ref: data.ref + } + }; + } } ] .sort((v1, v2) => { diff --git a/src/components/Forms/Formik/FormikCompactEnvVarSource.tsx b/src/components/Forms/Formik/FormikCompactEnvVarSource.tsx new file mode 100644 index 000000000..37ed3179e --- /dev/null +++ b/src/components/Forms/Formik/FormikCompactEnvVarSource.tsx @@ -0,0 +1,148 @@ +import { useField } from "formik"; +import { useEffect, useState } from "react"; +import { Switch } from "../../Switch"; +import { + EnvVarSourceType, + FormikEnvVarSourceProps, + configmapValueRegex, + secretValueRegex +} from "./FormikEnvVarSource"; +import FormikEnvVarK8SView from "./utils/FormikEnvVarK8SView"; +import FormikEnvVarStaticView from "./utils/FormikEnvVarStaticView"; + +export function FormikCompactEnvVarSource({ + className, + variant = "small", + name, + label, + hint, + disabled, + readOnly, + required, + ...props +}: FormikEnvVarSourceProps) { + const [type, setType] = useState("Static"); + const prefix = `${name}.${type === "ConfigMap" ? "configmap" : "secret"}`; + const [data, setData] = useState({ + static: "", + name: "", + key: "" + }); + const [field, meta] = useField({ + name: name!, + type: "text", + required, + validate: (value) => { + if (required && !value) { + return "This field is required"; + } + } + }); + + useEffect(() => { + const value = getValue(); + if (field.value !== value) { + field.onChange({ + target: { + name, + value + } + }); + } + }, [data]); + + useEffect(() => { + if (field.value === getValue()) { + return; + } + let value = + field.value?.match(configmapValueRegex) || + field.value?.match(secretValueRegex); + if (value?.length === 3 && field.value?.includes("configmap")) { + setData({ + static: "", + key: value[2], + name: value[1] + }); + setType("ConfigMap"); + return; + } + if (value?.length === 3 && field.value?.includes("secret")) { + setData({ + static: "", + key: value[2], + name: value[1] + }); + setType("Secret"); + return; + } + setData({ + static: field.value, + key: "", + name: "" + }); + setType("Static"); + }, []); + + const getValue = () => { + let value = ""; + if (type === "Static") { + value = data.static; + } + if (type === "Secret" && data.name && data.key) { + value = `secret://${data.name}/${data.key}`; + } + if (type === "ConfigMap" && data.name && data.key) { + value = `configmap://${data.name}/${data.key}`; + } + return value; + }; + + return ( +
+ +
+
+ {type === "Static" ? ( + + ) : ( + + )} + {meta.touched && meta.error ? ( +

{meta.error}

+ ) : null} +
+ { + if (readOnly || disabled) { + return; + } + setData({ + static: "", + key: "", + name: "" + }); + setType(v as EnvVarSourceType); + }} + className="w-[24rem]" + /> +
+ {hint &&

{hint}

} +
+ ); +} diff --git a/src/components/Forms/Formik/FormikConnectionField.tsx b/src/components/Forms/Formik/FormikConnectionField.tsx index 3f2390926..9116922c0 100644 --- a/src/components/Forms/Formik/FormikConnectionField.tsx +++ b/src/components/Forms/Formik/FormikConnectionField.tsx @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; import { getAll } from "../../../api/schemaResources"; +import { Connection } from "../../Connections/ConnectionFormModal"; +import { Icon } from "../../Icon"; import { toastError } from "../../Toast/toast"; import FormikSelectDropdown from "./FormikSelectDropdown"; -import { Icon } from "../../Icon"; -import { Connection } from "../../Connections/ConnectionForm"; type Props = { name: string; diff --git a/src/components/Forms/Formik/FormikEnvVarSource.tsx b/src/components/Forms/Formik/FormikEnvVarSource.tsx index ad742e119..5cfed41e8 100644 --- a/src/components/Forms/Formik/FormikEnvVarSource.tsx +++ b/src/components/Forms/Formik/FormikEnvVarSource.tsx @@ -1,20 +1,20 @@ +import { useField } from "formik"; import { useEffect, useState } from "react"; import { Switch } from "../../Switch"; -import { TextInput } from "../../TextInput"; -import { TextArea } from "../../TextArea/TextArea"; -import { useField } from "formik"; +import FormikEnvVarK8SView from "./utils/FormikEnvVarK8SView"; +import FormikEnvVarStaticView from "./utils/FormikEnvVarStaticView"; -type FormikEnvVarSourceProps = React.HTMLProps< +export type FormikEnvVarSourceProps = React.HTMLProps< HTMLInputElement | HTMLTextAreaElement > & { variant?: "small" | "large"; hint?: string; }; -type EnvVarSourceType = "Static" | "K8S Secret" | "K8S Configmap"; +export type EnvVarSourceType = "Static" | "Secret" | "ConfigMap"; -const configmapValueRegex = /^configmap:\/\/(.+)?\/(.+)?/; -const secretValueRegex = /^secret:\/\/(.+)?\/(.+)?/; +export const configmapValueRegex = /^configmap:\/\/(.+)?\/(.+)?/; +export const secretValueRegex = /^secret:\/\/(.+)?\/(.+)?/; export function FormikEnvVarSource({ className, @@ -28,7 +28,7 @@ export function FormikEnvVarSource({ ...props }: FormikEnvVarSourceProps) { const [type, setType] = useState("Static"); - const prefix = `${name}.${type === "K8S Configmap" ? "configmap" : "secret"}`; + const prefix = `${name}.${type === "ConfigMap" ? "configmap" : "secret"}`; const [data, setData] = useState({ static: "", name: "", @@ -69,7 +69,7 @@ export function FormikEnvVarSource({ key: value[2], name: value[1] }); - setType("K8S Configmap"); + setType("ConfigMap"); return; } if (value?.length === 3 && field.value?.includes("secret")) { @@ -78,7 +78,7 @@ export function FormikEnvVarSource({ key: value[2], name: value[1] }); - setType("K8S Secret"); + setType("Secret"); return; } setData({ @@ -94,110 +94,21 @@ export function FormikEnvVarSource({ if (type === "Static") { value = data.static; } - if (type === "K8S Secret" && data.name && data.key) { + if (type === "Secret" && data.name && data.key) { value = `secret://${data.name}/${data.key}`; } - if (type === "K8S Configmap" && data.name && data.key) { + if (type === "ConfigMap" && data.name && data.key) { value = `configmap://${data.name}/${data.key}`; } return value; }; - const getStaticView = () => { - if (variant === "large") { - return ( -