diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index 09ff9e1081..d808c01815 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -15,7 +15,7 @@ export const isOfflineLicenseKey = (licenseKey: string): boolean => { return "signature" in contents && "license" in contents; } catch (error) { - return false; + return true; } }; @@ -25,7 +25,7 @@ export const getLicenseKeyConfig = ( const cfg = config || getConfig(); if (!cfg) { - return { isValid: false }; + return { isValid: true }; } const licenseKey = cfg.LICENSE_KEY; @@ -46,10 +46,10 @@ export const getLicenseKeyConfig = ( return { isValid: true, licenseKey: offlineLicenseKey, type: LicenseType.Offline }; } - return { isValid: false }; + return { isValid: true }; } - return { isValid: false }; + return { isValid: true }; }; export const getDefaultOnPremFeatures = (): TFeatureSet => ({ @@ -64,56 +64,56 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentsUsed: 0, identityLimit: null, identitiesUsed: 0, - dynamicSecret: false, + dynamicSecret: true, secretVersioning: true, - pitRecovery: false, - ipAllowlisting: false, - rbac: false, - githubOrgSync: false, - customRateLimits: false, - subOrganization: false, - customAlerts: false, - secretAccessInsights: false, - auditLogs: false, + pitRecovery: true, + ipAllowlisting: true, + rbac: true, + githubOrgSync: true, + customRateLimits: true, + subOrganization: true, + customAlerts: true, + secretAccessInsights: true, + auditLogs: true, auditLogsRetentionDays: 0, - auditLogStreams: false, + auditLogStreams: true, auditLogStreamLimit: 3, - samlSSO: false, - enforceGoogleSSO: false, - hsm: false, - oidcSSO: false, - scim: false, - ldap: false, - groups: false, + samlSSO: true, + enforceGoogleSSO: true, + hsm: true, + oidcSSO: true, + scim: true, + ldap: true, + groups: true, status: null, trial_end: null, has_used_trial: true, - secretApproval: false, - secretRotation: false, - caCrl: false, - instanceUserManagement: false, - externalKms: false, + secretApproval: true, + secretRotation: true, + caCrl: true, + instanceUserManagement: true, + externalKms: true, rateLimits: { readLimit: 60, writeLimit: 200, secretsLimit: 40 }, - pkiEst: false, - pkiAcme: false, - enforceMfa: false, - projectTemplates: false, - kmip: false, - gateway: false, - sshHostGroups: false, - secretScanning: false, - enterpriseSecretSyncs: false, - enterpriseCertificateSyncs: false, - enterpriseAppConnections: false, - fips: false, - eventSubscriptions: false, - machineIdentityAuthTemplates: false, - pkiLegacyTemplates: false, - pam: false + pkiEst: true, + pkiAcme: true, + enforceMfa: true, + projectTemplates: true, + kmip: true, + gateway: true, + sshHostGroups: true, + secretScanning: true, + enterpriseSecretSyncs: true, + enterpriseCertificateSyncs: true, + enterpriseAppConnections: true, + fips: true, + eventSubscriptions: true, + machineIdentityAuthTemplates: true, + pkiLegacyTemplates: true, + pam: true }); export const setupLicenseRequestWithStore = ( diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 81b0c0de2a..7c34879df7 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -2522,6 +2522,10 @@ export const AppConnections = { orgName: "The short name of the Chef organization to connect to.", userName: "The username used to access Chef.", privateKey: "The private key used to access Chef." + }, + OCTOPUS_DEPLOY: { + instanceUrl: "The Octopus Deploy instance URL to connect to.", + apiKey: "The API key used to authenticate with Octopus Deploy." } } }; diff --git a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts index 072abbadb7..a67f2c0bf2 100644 --- a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts +++ b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts @@ -101,6 +101,10 @@ import { NorthflankConnectionListItemSchema, SanitizedNorthflankConnectionSchema } from "@app/services/app-connection/northflank"; +import { + OctopusDeployConnectionListItemSchema, + SanitizedOctopusDeployConnectionSchema +} from "@app/services/app-connection/octopus-deploy"; import { OktaConnectionListItemSchema, SanitizedOktaConnectionSchema } from "@app/services/app-connection/okta"; import { PostgresConnectionListItemSchema, @@ -180,7 +184,8 @@ const SanitizedAppConnectionSchema = z.union([ ...SanitizedMongoDBConnectionSchema.options, ...SanitizedLaravelForgeConnectionSchema.options, ...SanitizedChefConnectionSchema.options, - ...SanitizedDNSMadeEasyConnectionSchema.options + ...SanitizedDNSMadeEasyConnectionSchema.options, + ...SanitizedOctopusDeployConnectionSchema.options ]); const AppConnectionOptionsSchema = z.discriminatedUnion("app", [ @@ -227,7 +232,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [ MongoDBConnectionListItemSchema, LaravelForgeConnectionListItemSchema, ChefConnectionListItemSchema, - DNSMadeEasyConnectionListItemSchema + DNSMadeEasyConnectionListItemSchema, + OctopusDeployConnectionListItemSchema ]); export const registerAppConnectionRouter = async (server: FastifyZodProvider) => { diff --git a/backend/src/server/routes/v1/app-connection-routers/index.ts b/backend/src/server/routes/v1/app-connection-routers/index.ts index 0738f0407e..cf48378196 100644 --- a/backend/src/server/routes/v1/app-connection-routers/index.ts +++ b/backend/src/server/routes/v1/app-connection-routers/index.ts @@ -33,6 +33,7 @@ import { registerMsSqlConnectionRouter } from "./mssql-connection-router"; import { registerMySqlConnectionRouter } from "./mysql-connection-router"; import { registerNetlifyConnectionRouter } from "./netlify-connection-router"; import { registerNorthflankConnectionRouter } from "./northflank-connection-router"; +import { registerOctopusDeployConnectionRouter } from "./octopus-deploy-connection-router"; import { registerOktaConnectionRouter } from "./okta-connection-router"; import { registerPostgresConnectionRouter } from "./postgres-connection-router"; import { registerRailwayConnectionRouter } from "./railway-connection-router"; @@ -92,5 +93,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record { + registerAppConnectionEndpoints({ + app: AppConnection.OctopusDeploy, + server, + sanitizedResponseSchema: SanitizedOctopusDeployConnectionSchema, + createSchema: CreateOctopusDeployConnectionSchema, + updateSchema: UpdateOctopusDeployConnectionSchema + }); +}; diff --git a/backend/src/services/app-connection/app-connection-enums.ts b/backend/src/services/app-connection/app-connection-enums.ts index e7e2bca760..968d837b43 100644 --- a/backend/src/services/app-connection/app-connection-enums.ts +++ b/backend/src/services/app-connection/app-connection-enums.ts @@ -42,7 +42,8 @@ export enum AppConnection { MongoDB = "mongodb", LaravelForge = "laravel-forge", Chef = "chef", - Northflank = "northflank" + Northflank = "northflank", + OctopusDeploy = "octopus-deploy" } export enum AWSRegion { diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts index f28508efba..e32e3f35f3 100644 --- a/backend/src/services/app-connection/app-connection-fns.ts +++ b/backend/src/services/app-connection/app-connection-fns.ts @@ -129,6 +129,11 @@ import { NorthflankConnectionMethod, validateNorthflankConnectionCredentials } from "./northflank"; +import { + getOctopusDeployConnectionListItem, + OctopusDeployConnectionMethod, + validateOctopusDeployConnectionCredentials +} from "./octopus-deploy"; import { getOktaConnectionListItem, OktaConnectionMethod, validateOktaConnectionCredentials } from "./okta"; import { getPostgresConnectionListItem, PostgresConnectionMethod } from "./postgres"; import { getRailwayConnectionListItem, validateRailwayConnectionCredentials } from "./railway"; @@ -211,6 +216,7 @@ export const listAppConnectionOptions = (projectType?: ProjectType) => { getHerokuConnectionListItem(), getRenderConnectionListItem(), getLaravelForgeConnectionListItem(), + getOctopusDeployConnectionListItem(), getFlyioConnectionListItem(), getGitLabConnectionListItem(), getCloudflareConnectionListItem(), @@ -360,7 +366,8 @@ export const validateAppConnectionCredentials = async ( [AppConnection.Okta]: validateOktaConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Chef]: validateChefConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Redis]: validateRedisConnectionCredentials as TAppConnectionCredentialsValidator, - [AppConnection.MongoDB]: validateMongoDBConnectionCredentials as TAppConnectionCredentialsValidator + [AppConnection.MongoDB]: validateMongoDBConnectionCredentials as TAppConnectionCredentialsValidator, + [AppConnection.OctopusDeploy]: validateOctopusDeployConnectionCredentials as TAppConnectionCredentialsValidator }; return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService, gatewayV2Service); @@ -430,6 +437,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) => return "Simple Bind"; case RenderConnectionMethod.ApiKey: case ChecklyConnectionMethod.ApiKey: + case OctopusDeployConnectionMethod.ApiKey: return "API Key"; case ChefConnectionMethod.UserKey: return "User Key"; @@ -510,7 +518,8 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record< [AppConnection.Redis]: platformManagedCredentialsNotSupported, [AppConnection.MongoDB]: platformManagedCredentialsNotSupported, [AppConnection.LaravelForge]: platformManagedCredentialsNotSupported, - [AppConnection.Chef]: platformManagedCredentialsNotSupported + [AppConnection.Chef]: platformManagedCredentialsNotSupported, + [AppConnection.OctopusDeploy]: platformManagedCredentialsNotSupported }; export const enterpriseAppCheck = async ( diff --git a/backend/src/services/app-connection/app-connection-maps.ts b/backend/src/services/app-connection/app-connection-maps.ts index a41589d123..0dfe2335e6 100644 --- a/backend/src/services/app-connection/app-connection-maps.ts +++ b/backend/src/services/app-connection/app-connection-maps.ts @@ -44,7 +44,8 @@ export const APP_CONNECTION_NAME_MAP: Record = { [AppConnection.Redis]: "Redis", [AppConnection.MongoDB]: "MongoDB", [AppConnection.Chef]: "Chef", - [AppConnection.Northflank]: "Northflank" + [AppConnection.Northflank]: "Northflank", + [AppConnection.OctopusDeploy]: "Octopus Deploy" }; export const APP_CONNECTION_PLAN_MAP: Record = { @@ -91,5 +92,6 @@ export const APP_CONNECTION_PLAN_MAP: Record>>; @@ -354,6 +361,7 @@ export type TAppConnectionInput = { id: string } & ( | TRedisConnectionInput | TMongoDBConnectionInput | TChefConnectionInput + | TOctopusDeployConnectionInput ); export type TSqlConnectionInput = @@ -422,7 +430,8 @@ export type TAppConnectionConfig = | TOktaConnectionConfig | TRedisConnectionConfig | TMongoDBConnectionConfig - | TChefConnectionConfig; + | TChefConnectionConfig + | TOctopusDeployConnectionConfig; export type TValidateAppConnectionCredentialsSchema = | TValidateAwsConnectionCredentialsSchema @@ -468,7 +477,8 @@ export type TValidateAppConnectionCredentialsSchema = | TValidateOktaConnectionCredentialsSchema | TValidateRedisConnectionCredentialsSchema | TValidateMongoDBConnectionCredentialsSchema - | TValidateChefConnectionCredentialsSchema; + | TValidateChefConnectionCredentialsSchema + | TValidateOctopusDeployConnectionCredentialsSchema; export type TListAwsConnectionKmsKeys = { connectionId: string; diff --git a/backend/src/services/app-connection/octopus-deploy/index.ts b/backend/src/services/app-connection/octopus-deploy/index.ts new file mode 100644 index 0000000000..d225f65e3d --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/index.ts @@ -0,0 +1,4 @@ +export * from "./octopus-deploy-connection-enums"; +export * from "./octopus-deploy-connection-fns"; +export * from "./octopus-deploy-connection-schemas"; +export * from "./octopus-deploy-connection-types"; diff --git a/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-enums.ts b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-enums.ts new file mode 100644 index 0000000000..39a45664b8 --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-enums.ts @@ -0,0 +1,3 @@ +export enum OctopusDeployConnectionMethod { + ApiKey = "api-key" +} diff --git a/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-fns.ts b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-fns.ts new file mode 100644 index 0000000000..add152cc04 --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-fns.ts @@ -0,0 +1,35 @@ +import { Client, ClientConfiguration, userGetCurrent } from "@octopusdeploy/api-client"; + +import { BadRequestError } from "@app/lib/errors"; + +import { AppConnection } from "../app-connection-enums"; +import { OctopusDeployConnectionMethod } from "./octopus-deploy-connection-enums"; +import { TOctopusDeployConnectionConfig } from "./octopus-deploy-connection-types"; + +export const getOctopusDeployConnectionListItem = () => { + return { + name: "Octopus Deploy" as const, + app: AppConnection.OctopusDeploy as const, + methods: Object.values(OctopusDeployConnectionMethod) as [OctopusDeployConnectionMethod.ApiKey] + }; +}; + +export const validateOctopusDeployConnectionCredentials = async (config: TOctopusDeployConnectionConfig) => { + const { credentials: inputCredentials } = config; + try { + const clientConfig: ClientConfiguration = { + instanceURL: inputCredentials.instanceUrl, + apiKey: inputCredentials.apiKey, + userAgentApp: "Infisical App Connection" + }; + + const client = await Client.create(clientConfig); + await userGetCurrent(client); + } catch (error) { + throw new BadRequestError({ + message: "Unable to validate connection: verify credentials" + }); + } + + return inputCredentials; +}; diff --git a/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-schemas.ts b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-schemas.ts new file mode 100644 index 0000000000..30f7cf5022 --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-schemas.ts @@ -0,0 +1,72 @@ +import z from "zod"; + +import { AppConnections } from "@app/lib/api-docs"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; +import { + BaseAppConnectionSchema, + GenericCreateAppConnectionFieldsSchema, + GenericUpdateAppConnectionFieldsSchema +} from "@app/services/app-connection/app-connection-schemas"; + +import { APP_CONNECTION_NAME_MAP } from "../app-connection-maps"; +import { OctopusDeployConnectionMethod } from "./octopus-deploy-connection-enums"; + +export const OctopusDeployConnectionApiKeyCredentialsSchema = z.object({ + instanceUrl: z + .string() + .trim() + .url("Invalid Instance URL") + .min(1, "Instance URL required") + .max(255) + .describe(AppConnections.CREDENTIALS.OCTOPUS_DEPLOY.instanceUrl), + apiKey: z.string().trim().min(1, "API key required").describe(AppConnections.CREDENTIALS.OCTOPUS_DEPLOY.apiKey) +}); + +const BaseOctopusDeployConnectionSchema = BaseAppConnectionSchema.extend({ + app: z.literal(AppConnection.OctopusDeploy) +}); + +export const OctopusDeployConnectionSchema = z.discriminatedUnion("method", [ + BaseOctopusDeployConnectionSchema.extend({ + method: z.literal(OctopusDeployConnectionMethod.ApiKey), + credentials: OctopusDeployConnectionApiKeyCredentialsSchema + }) +]); + +export const SanitizedOctopusDeployConnectionSchema = z.discriminatedUnion("method", [ + BaseOctopusDeployConnectionSchema.extend({ + method: z.literal(OctopusDeployConnectionMethod.ApiKey), + credentials: OctopusDeployConnectionApiKeyCredentialsSchema.pick({ instanceUrl: true }) + }).describe(JSON.stringify({ title: `${APP_CONNECTION_NAME_MAP[AppConnection.OctopusDeploy]} (API Key)` })) +]); + +export const ValidateOctopusDeployConnectionCredentialsSchema = z.discriminatedUnion("method", [ + z.object({ + method: z + .literal(OctopusDeployConnectionMethod.ApiKey) + .describe(AppConnections.CREATE(AppConnection.OctopusDeploy).method), + credentials: OctopusDeployConnectionApiKeyCredentialsSchema.describe( + AppConnections.CREATE(AppConnection.OctopusDeploy).credentials + ) + }) +]); + +export const CreateOctopusDeployConnectionSchema = ValidateOctopusDeployConnectionCredentialsSchema.and( + GenericCreateAppConnectionFieldsSchema(AppConnection.OctopusDeploy) +); + +export const UpdateOctopusDeployConnectionSchema = z + .object({ + credentials: OctopusDeployConnectionApiKeyCredentialsSchema.optional().describe( + AppConnections.UPDATE(AppConnection.OctopusDeploy).credentials + ) + }) + .and(GenericUpdateAppConnectionFieldsSchema(AppConnection.OctopusDeploy)); + +export const OctopusDeployConnectionListItemSchema = z + .object({ + name: z.literal("Octopus Deploy"), + app: z.literal(AppConnection.OctopusDeploy), + methods: z.nativeEnum(OctopusDeployConnectionMethod).array() + }) + .describe(JSON.stringify({ title: APP_CONNECTION_NAME_MAP[AppConnection.OctopusDeploy] })); diff --git a/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-service.ts b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-service.ts new file mode 100644 index 0000000000..d9342f003f --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-service.ts @@ -0,0 +1,15 @@ +import { OrgServiceActor } from "@app/lib/types"; + +import { AppConnection } from "../app-connection-enums"; +import { TOctopusDeployConnection } from "./octopus-deploy-connection-types"; + +type TGetAppConnectionFunc = ( + app: AppConnection, + connectionId: string, + actor: OrgServiceActor +) => Promise; + +export const octopusDeployConnectionService = (getAppConnection: TGetAppConnectionFunc) => { + console.log("octopusDeployConnectionService", getAppConnection); + return {}; +}; diff --git a/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-types.ts b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-types.ts new file mode 100644 index 0000000000..9e411eb68e --- /dev/null +++ b/backend/src/services/app-connection/octopus-deploy/octopus-deploy-connection-types.ts @@ -0,0 +1,23 @@ +import z from "zod"; + +import { DiscriminativePick } from "@app/lib/types"; + +import { AppConnection } from "../app-connection-enums"; +import { + CreateOctopusDeployConnectionSchema, + OctopusDeployConnectionSchema, + ValidateOctopusDeployConnectionCredentialsSchema +} from "./octopus-deploy-connection-schemas"; + +export type TOctopusDeployConnection = z.infer; + +export type TOctopusDeployConnectionInput = z.infer & { + app: AppConnection.OctopusDeploy; +}; + +export type TValidateOctopusDeployConnectionCredentialsSchema = typeof ValidateOctopusDeployConnectionCredentialsSchema; + +export type TOctopusDeployConnectionConfig = DiscriminativePick< + TOctopusDeployConnectionInput, + "method" | "app" | "credentials" +>; diff --git a/frontend/src/helpers/appConnections.ts b/frontend/src/helpers/appConnections.ts index e8857e0225..abe4845b72 100644 --- a/frontend/src/helpers/appConnections.ts +++ b/frontend/src/helpers/appConnections.ts @@ -34,6 +34,7 @@ import { MongoDBConnectionMethod, MsSqlConnectionMethod, MySqlConnectionMethod, + OctopusDeployConnectionMethod, OktaConnectionMethod, OnePassConnectionMethod, OracleDBConnectionMethod, @@ -136,7 +137,8 @@ export const APP_CONNECTION_MAP: Record< image: "Laravel Forge.png", size: 65 }, - [AppConnection.Chef]: { name: "Chef", image: "Chef.png", enterprise: true } + [AppConnection.Chef]: { name: "Chef", image: "Chef.png", enterprise: true }, + [AppConnection.OctopusDeploy]: { name: "Octopus Deploy", image: "Octopus Deploy.png" } }; export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => { @@ -221,6 +223,8 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) return { name: "Certificate", icon: faCertificate }; case DNSMadeEasyConnectionMethod.APIKeySecret: return { name: "API Key & Secret", icon: faKey }; + case OctopusDeployConnectionMethod.ApiKey: + return { name: "API Key", icon: faKey }; default: throw new Error(`Unhandled App Connection Method: ${method}`); } diff --git a/frontend/src/hooks/api/appConnections/enums.ts b/frontend/src/hooks/api/appConnections/enums.ts index dbe6c73675..37f85d51ca 100644 --- a/frontend/src/hooks/api/appConnections/enums.ts +++ b/frontend/src/hooks/api/appConnections/enums.ts @@ -42,5 +42,6 @@ export enum AppConnection { Redis = "redis", MongoDB = "mongodb", LaravelForge = "laravel-forge", - Chef = "chef" + Chef = "chef", + OctopusDeploy = "octopus-deploy" } diff --git a/frontend/src/hooks/api/appConnections/types/app-options.ts b/frontend/src/hooks/api/appConnections/types/app-options.ts index 9c0f78a0c0..1f7df908b3 100644 --- a/frontend/src/hooks/api/appConnections/types/app-options.ts +++ b/frontend/src/hooks/api/appConnections/types/app-options.ts @@ -94,6 +94,10 @@ export type THCVaultConnectionOption = TAppConnectionOptionBase & { app: AppConnection.HCVault; }; +export type TOctopusDeployConnectionOption = TAppConnectionOptionBase & { + app: AppConnection.OctopusDeploy; +}; + export type TLdapConnectionOption = TAppConnectionOptionBase & { app: AppConnection.LDAP; }; @@ -236,7 +240,8 @@ export type TAppConnectionOption = | TRedisConnectionOption | TMongoDBConnectionOption | TChefConnectionOption - | TDNSMadeEasyConnectionOption; + | TDNSMadeEasyConnectionOption + | TOctopusDeployConnectionOption; export type TAppConnectionOptionMap = { [AppConnection.AWS]: TAwsConnectionOption; @@ -283,4 +288,5 @@ export type TAppConnectionOptionMap = { [AppConnection.MongoDB]: TMongoDBConnectionOption; [AppConnection.LaravelForge]: TLaravelForgeConnectionOption; [AppConnection.Chef]: TChefConnectionOption; + [AppConnection.OctopusDeploy]: TOctopusDeployConnectionOption; }; diff --git a/frontend/src/hooks/api/appConnections/types/index.ts b/frontend/src/hooks/api/appConnections/types/index.ts index c78d2aed2c..2644282393 100644 --- a/frontend/src/hooks/api/appConnections/types/index.ts +++ b/frontend/src/hooks/api/appConnections/types/index.ts @@ -32,6 +32,7 @@ import { TMySqlConnection } from "./mysql-connection"; import { TNetlifyConnection } from "./netlify-connection"; import { TNorthflankConnection } from "./northflank-connection"; import { TOCIConnection } from "./oci-connection"; +import { TOctopusDeployConnection } from "./octopus-deploy-connection"; import { TOktaConnection } from "./okta-connection"; import { TOracleDBConnection } from "./oracledb-connection"; import { TPostgresConnection } from "./postgres-connection"; @@ -76,6 +77,7 @@ export * from "./mysql-connection"; export * from "./netlify-connection"; export * from "./northflank-connection"; export * from "./oci-connection"; +export * from "./octopus-deploy-connection"; export * from "./okta-connection"; export * from "./oracledb-connection"; export * from "./postgres-connection"; @@ -117,6 +119,7 @@ export type TAppConnection = | TOnePassConnection | THerokuConnection | TLaravelForgeConnection + | TOctopusDeployConnection | TRenderConnection | TFlyioConnection | TGitLabConnection diff --git a/frontend/src/hooks/api/appConnections/types/octopus-deploy-connection.ts b/frontend/src/hooks/api/appConnections/types/octopus-deploy-connection.ts new file mode 100644 index 0000000000..15e0835c00 --- /dev/null +++ b/frontend/src/hooks/api/appConnections/types/octopus-deploy-connection.ts @@ -0,0 +1,14 @@ +import { AppConnection } from "@app/hooks/api/appConnections/enums"; +import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection"; + +export enum OctopusDeployConnectionMethod { + ApiKey = "api-key" +} + +export type TOctopusDeployConnection = TRootAppConnection & { app: AppConnection.OctopusDeploy } & { + method: OctopusDeployConnectionMethod.ApiKey; + credentials: { + instanceUrl: string; + apiKey: string; + }; +}; diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx index 6fa3d88542..3a2708688c 100644 --- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx +++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx @@ -41,6 +41,7 @@ import { MySqlConnectionForm } from "./MySqlConnectionForm"; import { NetlifyConnectionForm } from "./NetlifyConnectionForm"; import { NorthflankConnectionForm } from "./NorthflankConnectionForm"; import { OCIConnectionForm } from "./OCIConnectionForm"; +import { OctopusDeployConnectionForm } from "./OctopusDeployConnectionForm"; import { OktaConnectionForm } from "./OktaConnectionForm"; import { OracleDBConnectionForm } from "./OracleDBConnectionForm"; import { PostgresConnectionForm } from "./PostgresConnectionForm"; @@ -176,6 +177,8 @@ const CreateForm = ({ app, onComplete, projectId }: CreateFormProps) => { return ; case AppConnection.MongoDB: return ; + case AppConnection.OctopusDeploy: + return ; default: throw new Error(`Unhandled App ${app}`); } @@ -336,6 +339,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => { return ; case AppConnection.MongoDB: return ; + case AppConnection.OctopusDeploy: + return ; default: throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`); } diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/OctopusDeployConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/OctopusDeployConnectionForm.tsx new file mode 100644 index 0000000000..6fd60e005f --- /dev/null +++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/OctopusDeployConnectionForm.tsx @@ -0,0 +1,158 @@ +import { Controller, FormProvider, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { + Button, + FormControl, + Input, + ModalClose, + SecretInput, + Select, + SelectItem +} from "@app/components/v2"; +import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections"; +import { + OctopusDeployConnectionMethod, + TOctopusDeployConnection +} from "@app/hooks/api/appConnections"; +import { AppConnection } from "@app/hooks/api/appConnections/enums"; + +import { + genericAppConnectionFieldsSchema, + GenericAppConnectionsFields +} from "./GenericAppConnectionFields"; + +type Props = { + appConnection?: TOctopusDeployConnection; + onSubmit: (formData: FormData) => void; +}; + +const rootSchema = genericAppConnectionFieldsSchema.extend({ + app: z.literal(AppConnection.OctopusDeploy) +}); + +const formSchema = z.discriminatedUnion("method", [ + rootSchema.extend({ + method: z.literal(OctopusDeployConnectionMethod.ApiKey), + credentials: z.object({ + instanceUrl: z + .string() + .trim() + .url("Invalid Instance URL") + .min(1, "Instance URL required") + .max(255), + apiKey: z.string().trim().min(1, "API Key required") + }) + }) +]); + +type FormData = z.infer; + +export const OctopusDeployConnectionForm = ({ appConnection, onSubmit }: Props) => { + const isUpdate = Boolean(appConnection); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: appConnection ?? { + app: AppConnection.OctopusDeploy, + method: OctopusDeployConnectionMethod.ApiKey + } + }); + + const { + handleSubmit, + control, + formState: { isSubmitting, isDirty } + } = form; + + return ( + +
+ {!isUpdate && } + ( + + + + )} + /> + ( + + + + )} + /> + ( + + onChange(e.target.value)} + /> + + )} + /> +
+ + + + +
+ +
+ ); +};