mirror of
https://github.com/Infisical/infisical.git
synced 2026-05-02 03:02:03 -04:00
feat: added camunda app connection
This commit is contained in:
@@ -12,6 +12,10 @@ import {
|
||||
AzureKeyVaultConnectionListItemSchema,
|
||||
SanitizedAzureKeyVaultConnectionSchema
|
||||
} from "@app/services/app-connection/azure-key-vault";
|
||||
import {
|
||||
CamundaConnectionListItemSchema,
|
||||
SanitizedCamundaConnectionSchema
|
||||
} from "@app/services/app-connection/camunda";
|
||||
import {
|
||||
DatabricksConnectionListItemSchema,
|
||||
SanitizedDatabricksConnectionSchema
|
||||
@@ -39,7 +43,8 @@ const SanitizedAppConnectionSchema = z.union([
|
||||
...SanitizedDatabricksConnectionSchema.options,
|
||||
...SanitizedHumanitecConnectionSchema.options,
|
||||
...SanitizedPostgresConnectionSchema.options,
|
||||
...SanitizedMsSqlConnectionSchema.options
|
||||
...SanitizedMsSqlConnectionSchema.options,
|
||||
...SanitizedCamundaConnectionSchema.options
|
||||
]);
|
||||
|
||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
@@ -51,7 +56,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
DatabricksConnectionListItemSchema,
|
||||
HumanitecConnectionListItemSchema,
|
||||
PostgresConnectionListItemSchema,
|
||||
MsSqlConnectionListItemSchema
|
||||
MsSqlConnectionListItemSchema,
|
||||
CamundaConnectionListItemSchema
|
||||
]);
|
||||
|
||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateCamundaConnectionSchema,
|
||||
SanitizedCamundaConnectionSchema,
|
||||
UpdateCamundaConnectionSchema
|
||||
} from "@app/services/app-connection/camunda";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerCamundaConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.Camunda,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedCamundaConnectionSchema,
|
||||
createSchema: CreateCamundaConnectionSchema,
|
||||
updateSchema: UpdateCamundaConnectionSchema
|
||||
});
|
||||
|
||||
// The below endpoints are not exposed and for Infisical App use
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/clusters`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
clusters: z.object({ uuid: z.string(), name: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const clusters = await server.services.appConnection.camunda.listClusters(connectionId, req.permission);
|
||||
|
||||
return { clusters };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
import { registerAwsConnectionRouter } from "./aws-connection-router";
|
||||
import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router";
|
||||
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
|
||||
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||
@@ -22,5 +23,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
||||
[AppConnection.Databricks]: registerDatabricksConnectionRouter,
|
||||
[AppConnection.Humanitec]: registerHumanitecConnectionRouter,
|
||||
[AppConnection.Postgres]: registerPostgresConnectionRouter,
|
||||
[AppConnection.MsSql]: registerMsSqlConnectionRouter
|
||||
[AppConnection.MsSql]: registerMsSqlConnectionRouter,
|
||||
[AppConnection.Camunda]: registerCamundaConnectionRouter
|
||||
};
|
||||
|
||||
@@ -7,7 +7,8 @@ export enum AppConnection {
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
Humanitec = "humanitec",
|
||||
Postgres = "postgres",
|
||||
MsSql = "mssql"
|
||||
MsSql = "mssql",
|
||||
Camunda = "camunda"
|
||||
}
|
||||
|
||||
export enum AWSRegion {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
getAzureKeyVaultConnectionListItem,
|
||||
validateAzureKeyVaultConnectionCredentials
|
||||
} from "./azure-key-vault";
|
||||
import { CamundaConnectionMethod, getCamundaConnectionListItem, validateCamundaConnectionCredentials } from "./camunda";
|
||||
import {
|
||||
DatabricksConnectionMethod,
|
||||
getDatabricksConnectionListItem,
|
||||
@@ -52,7 +53,8 @@ export const listAppConnectionOptions = () => {
|
||||
getDatabricksConnectionListItem(),
|
||||
getHumanitecConnectionListItem(),
|
||||
getPostgresConnectionListItem(),
|
||||
getMsSqlConnectionListItem()
|
||||
getMsSqlConnectionListItem(),
|
||||
getCamundaConnectionListItem()
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
@@ -108,7 +110,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TAppConnect
|
||||
validateAzureAppConfigurationConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Humanitec]: validateHumanitecConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Postgres]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator
|
||||
[AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||
[AppConnection.Camunda]: validateCamundaConnectionCredentials as TAppConnectionCredentialsValidator
|
||||
};
|
||||
|
||||
export const validateAppConnectionCredentials = async (
|
||||
@@ -131,6 +134,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
return "Service Account Impersonation";
|
||||
case DatabricksConnectionMethod.ServicePrincipal:
|
||||
return "Service Principal";
|
||||
case CamundaConnectionMethod.ClientCredentials:
|
||||
return "Client Credentials";
|
||||
case HumanitecConnectionMethod.ApiToken:
|
||||
return "API Token";
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
@@ -175,5 +180,6 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
||||
[AppConnection.AzureAppConfiguration]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Humanitec]: platformManagedCredentialsNotSupported,
|
||||
[AppConnection.Postgres]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
|
||||
[AppConnection.MsSql]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform
|
||||
[AppConnection.MsSql]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
|
||||
[AppConnection.Camunda]: platformManagedCredentialsNotSupported
|
||||
};
|
||||
|
||||
@@ -31,6 +31,8 @@ import { ValidateAwsConnectionCredentialsSchema } from "./aws";
|
||||
import { awsConnectionService } from "./aws/aws-connection-service";
|
||||
import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration";
|
||||
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
||||
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
|
||||
import { databricksConnectionService } from "./databricks/databricks-connection-service";
|
||||
import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
||||
@@ -59,7 +61,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema,
|
||||
[AppConnection.Humanitec]: ValidateHumanitecConnectionCredentialsSchema,
|
||||
[AppConnection.Postgres]: ValidatePostgresConnectionCredentialsSchema,
|
||||
[AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema
|
||||
[AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema,
|
||||
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema
|
||||
};
|
||||
|
||||
export const appConnectionServiceFactory = ({
|
||||
@@ -430,6 +433,7 @@ export const appConnectionServiceFactory = ({
|
||||
gcp: gcpConnectionService(connectAppConnectionById),
|
||||
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||
aws: awsConnectionService(connectAppConnectionById),
|
||||
humanitec: humanitecConnectionService(connectAppConnectionById)
|
||||
humanitec: humanitecConnectionService(connectAppConnectionById),
|
||||
camunda: camundaConnectionService(connectAppConnectionById, appConnectionDAL, kmsService)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -21,6 +21,12 @@ import {
|
||||
TAzureKeyVaultConnectionInput,
|
||||
TValidateAzureKeyVaultConnectionCredentialsSchema
|
||||
} from "./azure-key-vault";
|
||||
import {
|
||||
TCamundaConnection,
|
||||
TCamundaConnectionConfig,
|
||||
TCamundaConnectionInput,
|
||||
TValidateCamundaConnectionCredentialsSchema
|
||||
} from "./camunda";
|
||||
import {
|
||||
TDatabricksConnection,
|
||||
TDatabricksConnectionConfig,
|
||||
@@ -62,6 +68,7 @@ export type TAppConnection = { id: string } & (
|
||||
| THumanitecConnection
|
||||
| TPostgresConnection
|
||||
| TMsSqlConnection
|
||||
| TCamundaConnection
|
||||
);
|
||||
|
||||
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
|
||||
@@ -78,6 +85,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| THumanitecConnectionInput
|
||||
| TPostgresConnectionInput
|
||||
| TMsSqlConnectionInput
|
||||
| TCamundaConnectionInput
|
||||
);
|
||||
|
||||
export type TSqlConnectionInput = TPostgresConnectionInput | TMsSqlConnectionInput;
|
||||
@@ -99,7 +107,8 @@ export type TAppConnectionConfig =
|
||||
| TAzureAppConfigurationConnectionConfig
|
||||
| TDatabricksConnectionConfig
|
||||
| THumanitecConnectionConfig
|
||||
| TSqlConnectionConfig;
|
||||
| TSqlConnectionConfig
|
||||
| TCamundaConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentialsSchema =
|
||||
| TValidateAwsConnectionCredentialsSchema
|
||||
@@ -110,7 +119,8 @@ export type TValidateAppConnectionCredentialsSchema =
|
||||
| TValidateDatabricksConnectionCredentialsSchema
|
||||
| TValidateHumanitecConnectionCredentialsSchema
|
||||
| TValidatePostgresConnectionCredentialsSchema
|
||||
| TValidateMsSqlConnectionCredentialsSchema;
|
||||
| TValidateMsSqlConnectionCredentialsSchema
|
||||
| TValidateCamundaConnectionCredentialsSchema;
|
||||
|
||||
export type TListAwsConnectionKmsKeys = {
|
||||
connectionId: string;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum CamundaConnectionMethod {
|
||||
ClientCredentials = "client-credentials"
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { encryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { TAppConnectionDALFactory } from "../app-connection-dal";
|
||||
import { CamundaConnectionMethod } from "./camunda-connection-enums";
|
||||
import { TAuthorizeCamundaConnection, TCamundaConnection, TCamundaConnectionConfig } from "./camunda-connection-types";
|
||||
|
||||
export const getCamundaConnectionListItem = () => {
|
||||
return {
|
||||
name: "Camunda" as const,
|
||||
app: AppConnection.Camunda as const,
|
||||
methods: Object.values(CamundaConnectionMethod) as [CamundaConnectionMethod.ClientCredentials]
|
||||
};
|
||||
};
|
||||
|
||||
const authorizeCamundaConnection = async ({
|
||||
clientId,
|
||||
clientSecret
|
||||
}: Pick<TCamundaConnection["credentials"], "clientId" | "clientSecret">) => {
|
||||
const { data } = await request.post<TAuthorizeCamundaConnection>(
|
||||
IntegrationUrls.CAMUNDA_TOKEN_URL,
|
||||
{
|
||||
grant_type: "client_credentials",
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
audience: "api.cloud.camunda.io"
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken: data.access_token, expiresAt: data.expires_in * 1000 + Date.now() };
|
||||
};
|
||||
|
||||
export const getCamundaConnectionAccessToken = async (
|
||||
{ id, orgId, credentials }: TCamundaConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const { clientSecret, clientId, accessToken, expiresAt } = credentials;
|
||||
|
||||
// get new token if less than 10 minutes from expiry
|
||||
if (Date.now() < expiresAt - 10_000) {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
const authData = await authorizeCamundaConnection({ clientId, clientSecret });
|
||||
|
||||
const updatedCredentials: TCamundaConnection["credentials"] = {
|
||||
...credentials,
|
||||
...authData
|
||||
};
|
||||
|
||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: updatedCredentials,
|
||||
orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
await appConnectionDAL.updateById(id, { encryptedCredentials });
|
||||
|
||||
return authData.accessToken;
|
||||
};
|
||||
|
||||
export const validateCamundaConnectionCredentials = async (appConnection: TCamundaConnectionConfig) => {
|
||||
const { credentials } = appConnection;
|
||||
|
||||
try {
|
||||
const { accessToken, expiresAt } = await authorizeCamundaConnection(appConnection.credentials);
|
||||
|
||||
return {
|
||||
...credentials,
|
||||
accessToken,
|
||||
expiresAt
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
throw new BadRequestError({
|
||||
message: `Unable to validate connection: verify credentials`
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
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 { CamundaConnectionMethod } from "./camunda-connection-enums";
|
||||
|
||||
const BaseCamundaConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Camunda) });
|
||||
|
||||
export const CamundaConnectionClientCredentialsInputCredentialsSchema = z.object({
|
||||
clientId: z.string().trim().min(1, "Client ID required"),
|
||||
clientSecret: z.string().trim().min(1, "Client Secret required")
|
||||
});
|
||||
|
||||
export const CamundaConnectionClientCredentialsOutputCredentialsSchema = z
|
||||
.object({
|
||||
accessToken: z.string(),
|
||||
expiresAt: z.number()
|
||||
})
|
||||
.merge(CamundaConnectionClientCredentialsInputCredentialsSchema);
|
||||
|
||||
export const CamundaConnectionSchema = z.intersection(
|
||||
BaseCamundaConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(CamundaConnectionMethod.ClientCredentials),
|
||||
credentials: CamundaConnectionClientCredentialsOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedCamundaConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseCamundaConnectionSchema.extend({
|
||||
method: z.literal(CamundaConnectionMethod.ClientCredentials),
|
||||
credentials: CamundaConnectionClientCredentialsOutputCredentialsSchema.pick({
|
||||
clientId: true
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateCamundaConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z
|
||||
.literal(CamundaConnectionMethod.ClientCredentials)
|
||||
.describe(AppConnections.CREATE(AppConnection.Camunda).method),
|
||||
credentials: CamundaConnectionClientCredentialsInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.Camunda).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateCamundaConnectionSchema = ValidateCamundaConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.Camunda)
|
||||
);
|
||||
|
||||
export const UpdateCamundaConnectionSchema = z
|
||||
.object({
|
||||
credentials: CamundaConnectionClientCredentialsInputCredentialsSchema.optional().describe(
|
||||
AppConnections.UPDATE(AppConnection.Camunda).credentials
|
||||
)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Camunda));
|
||||
|
||||
export const CamundaConnectionListItemSchema = z.object({
|
||||
name: z.literal("Camunda"),
|
||||
app: z.literal(AppConnection.Camunda),
|
||||
methods: z.nativeEnum(CamundaConnectionMethod).array()
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { getCamundaConnectionAccessToken } from "./camunda-connection-fns";
|
||||
import { TCamundaConnection, TCamundaListClustersResponse } from "./camunda-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
connectionId: string,
|
||||
actor: OrgServiceActor
|
||||
) => Promise<TCamundaConnection>;
|
||||
|
||||
const listCamundaClusters = async (
|
||||
appConnection: TCamundaConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const accessToken = await getCamundaConnectionAccessToken(appConnection, appConnectionDAL, kmsService);
|
||||
|
||||
const { data } = await request.get<TCamundaListClustersResponse>(`${IntegrationUrls.CAMUNDA_API_URL}/clusters`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
return data ?? [];
|
||||
};
|
||||
|
||||
export const camundaConnectionService = (
|
||||
getAppConnection: TGetAppConnectionFunc,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const listClusters = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Camunda, connectionId, actor);
|
||||
|
||||
const clusters = await listCamundaClusters(appConnection, appConnectionDAL, kmsService);
|
||||
|
||||
return clusters;
|
||||
};
|
||||
|
||||
return {
|
||||
listClusters
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import {
|
||||
CamundaConnectionSchema,
|
||||
CreateCamundaConnectionSchema,
|
||||
ValidateCamundaConnectionCredentialsSchema
|
||||
} from "./camunda-connection-schema";
|
||||
|
||||
export type TCamundaConnection = z.infer<typeof CamundaConnectionSchema>;
|
||||
|
||||
export type TCamundaConnectionInput = z.infer<typeof CreateCamundaConnectionSchema> & {
|
||||
app: AppConnection.Camunda;
|
||||
};
|
||||
|
||||
export type TValidateCamundaConnectionCredentialsSchema = typeof ValidateCamundaConnectionCredentialsSchema;
|
||||
|
||||
export type TCamundaConnectionConfig = DiscriminativePick<TCamundaConnectionInput, "method" | "app" | "credentials"> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TAuthorizeCamundaConnection = {
|
||||
access_token: string;
|
||||
scope: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
};
|
||||
|
||||
export type TCamundaListClustersResponse = { uuid: string; name: string }[];
|
||||
4
backend/src/services/app-connection/camunda/index.ts
Normal file
4
backend/src/services/app-connection/camunda/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./camunda-connection-enums";
|
||||
export * from "./camunda-connection-fns";
|
||||
export * from "./camunda-connection-schema";
|
||||
export * from "./camunda-connection-types";
|
||||
@@ -63,6 +63,7 @@ export enum IntegrationUrls {
|
||||
GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token",
|
||||
GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token",
|
||||
BITBUCKET_TOKEN_URL = "https://bitbucket.org/site/oauth2/access_token",
|
||||
CAMUNDA_TOKEN_URL = "https://login.cloud.camunda.io/oauth/token",
|
||||
|
||||
// integration apps endpoints
|
||||
GCP_API_URL = "https://cloudresourcemanager.googleapis.com",
|
||||
@@ -94,6 +95,7 @@ export enum IntegrationUrls {
|
||||
HASURA_CLOUD_API_URL = "https://data.pro.hasura.io/v1/graphql",
|
||||
AZURE_DEVOPS_API_URL = "https://dev.azure.com",
|
||||
HUMANITEC_API_URL = "https://api.humanitec.io",
|
||||
CAMUNDA_API_URL = "https://api.cloud.camunda.io",
|
||||
|
||||
GCP_SECRET_MANAGER_SERVICE_NAME = "secretmanager.googleapis.com",
|
||||
GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`,
|
||||
|
||||
BIN
frontend/public/images/integrations/Camunda.png
Normal file
BIN
frontend/public/images/integrations/Camunda.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
@@ -6,6 +6,7 @@ import {
|
||||
AwsConnectionMethod,
|
||||
AzureAppConfigurationConnectionMethod,
|
||||
AzureKeyVaultConnectionMethod,
|
||||
CamundaConnectionMethod,
|
||||
DatabricksConnectionMethod,
|
||||
GcpConnectionMethod,
|
||||
GitHubConnectionMethod,
|
||||
@@ -30,7 +31,8 @@ export const APP_CONNECTION_MAP: Record<AppConnection, { name: string; image: st
|
||||
[AppConnection.Databricks]: { name: "Databricks", image: "Databricks.png" },
|
||||
[AppConnection.Humanitec]: { name: "Humanitec", image: "Humanitec.png" },
|
||||
[AppConnection.Postgres]: { name: "PostgreSQL", image: "Postgres.png" },
|
||||
[AppConnection.MsSql]: { name: "Microsoft SQL Server", image: "MsSql.png" }
|
||||
[AppConnection.MsSql]: { name: "Microsoft SQL Server", image: "MsSql.png" },
|
||||
[AppConnection.Camunda]: { name: "Camunda", image: "Camunda.png" }
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||
@@ -49,6 +51,8 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
||||
return { name: "Service Account Impersonation", icon: faUser };
|
||||
case DatabricksConnectionMethod.ServicePrincipal:
|
||||
return { name: "Service Principal", icon: faUser };
|
||||
case CamundaConnectionMethod.ClientCredentials:
|
||||
return { name: "Client Credentials", icon: faKey };
|
||||
case HumanitecConnectionMethod.ApiToken:
|
||||
return { name: "API Token", icon: faKey };
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
|
||||
@@ -7,5 +7,6 @@ export enum AppConnection {
|
||||
Databricks = "databricks",
|
||||
Humanitec = "humanitec",
|
||||
Postgres = "postgres",
|
||||
MsSql = "mssql"
|
||||
MsSql = "mssql",
|
||||
Camunda = "camunda"
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ export type TMsSqlConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.MsSql;
|
||||
};
|
||||
|
||||
export type TCamundaConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.Camunda;
|
||||
};
|
||||
|
||||
export type TAppConnectionOption =
|
||||
| TAwsConnectionOption
|
||||
| TGitHubConnectionOption
|
||||
@@ -56,7 +60,8 @@ export type TAppConnectionOption =
|
||||
| TDatabricksConnectionOption
|
||||
| THumanitecConnectionOption
|
||||
| TPostgresConnectionOption
|
||||
| TMsSqlConnectionOption;
|
||||
| TMsSqlConnectionOption
|
||||
| TCamundaConnectionOption;
|
||||
|
||||
export type TAppConnectionOptionMap = {
|
||||
[AppConnection.AWS]: TAwsConnectionOption;
|
||||
@@ -68,4 +73,5 @@ export type TAppConnectionOptionMap = {
|
||||
[AppConnection.Humanitec]: THumanitecConnectionOption;
|
||||
[AppConnection.Postgres]: TPostgresConnectionOption;
|
||||
[AppConnection.MsSql]: TMsSqlConnectionOption;
|
||||
[AppConnection.Camunda]: TCamundaConnectionOption;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { AppConnection } from "../enums";
|
||||
import { TRootAppConnection } from "./root-connection";
|
||||
|
||||
export enum CamundaConnectionMethod {
|
||||
ClientCredentials = "client-credentials"
|
||||
}
|
||||
|
||||
export type TCamundaConnection = TRootAppConnection & { app: AppConnection.Camunda } & {
|
||||
method: CamundaConnectionMethod.ClientCredentials;
|
||||
credentials: {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
};
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { TAppConnectionOption } from "./app-options";
|
||||
import { TAwsConnection } from "./aws-connection";
|
||||
import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection";
|
||||
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
|
||||
import { TCamundaConnection } from "./camunda-connection";
|
||||
import { TDatabricksConnection } from "./databricks-connection";
|
||||
import { TGcpConnection } from "./gcp-connection";
|
||||
import { TGitHubConnection } from "./github-connection";
|
||||
@@ -13,6 +14,7 @@ import { TPostgresConnection } from "./postgres-connection";
|
||||
export * from "./aws-connection";
|
||||
export * from "./azure-app-configuration-connection";
|
||||
export * from "./azure-key-vault-connection";
|
||||
export * from "./camunda-connection";
|
||||
export * from "./databricks-connection";
|
||||
export * from "./gcp-connection";
|
||||
export * from "./github-connection";
|
||||
@@ -29,7 +31,8 @@ export type TAppConnection =
|
||||
| TDatabricksConnection
|
||||
| THumanitecConnection
|
||||
| TPostgresConnection
|
||||
| TMsSqlConnection;
|
||||
| TMsSqlConnection
|
||||
| TCamundaConnection;
|
||||
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||
|
||||
@@ -66,4 +69,5 @@ export type TAppConnectionMap = {
|
||||
[AppConnection.Humanitec]: THumanitecConnection;
|
||||
[AppConnection.Postgres]: TPostgresConnection;
|
||||
[AppConnection.MsSql]: TMsSqlConnection;
|
||||
[AppConnection.Camunda]: TCamundaConnection;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import { AppConnectionHeader } from "../AppConnectionHeader";
|
||||
import { AwsConnectionForm } from "./AwsConnectionForm";
|
||||
import { AzureAppConfigurationConnectionForm } from "./AzureAppConfigurationConnectionForm";
|
||||
import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm";
|
||||
import { CamundaConnectionForm } from "./CamundaConnectionForm";
|
||||
import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
|
||||
import { GcpConnectionForm } from "./GcpConnectionForm";
|
||||
import { GitHubConnectionForm } from "./GitHubConnectionForm";
|
||||
@@ -74,6 +75,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
|
||||
return <PostgresConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.MsSql:
|
||||
return <MsSqlConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.Camunda:
|
||||
return <CamundaConnectionForm onSubmit={onSubmit} />;
|
||||
default:
|
||||
throw new Error(`Unhandled App ${app}`);
|
||||
}
|
||||
@@ -128,6 +131,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
|
||||
return <PostgresConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.MsSql:
|
||||
return <MsSqlConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.Camunda:
|
||||
return <CamundaConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
default:
|
||||
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
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 { CamundaConnectionMethod, TCamundaConnection } from "@app/hooks/api/appConnections";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
|
||||
import {
|
||||
genericAppConnectionFieldsSchema,
|
||||
GenericAppConnectionsFields
|
||||
} from "./GenericAppConnectionFields";
|
||||
|
||||
type Props = {
|
||||
appConnection?: TCamundaConnection;
|
||||
onSubmit: (formData: FormData) => Promise<void>;
|
||||
};
|
||||
|
||||
const rootSchema = genericAppConnectionFieldsSchema.extend({
|
||||
app: z.literal(AppConnection.Camunda)
|
||||
});
|
||||
|
||||
const formSchema = z.discriminatedUnion("method", [
|
||||
rootSchema.extend({
|
||||
method: z.literal(CamundaConnectionMethod.ClientCredentials),
|
||||
credentials: z.object({
|
||||
clientId: z.string().trim().min(1, "Client ID required"),
|
||||
clientSecret: z.string().trim().min(1, "Client secret required")
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
export const CamundaConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
const isUpdate = Boolean(appConnection);
|
||||
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.Camunda,
|
||||
method: CamundaConnectionMethod.ClientCredentials
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = form;
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{!isUpdate && <GenericAppConnectionsFields />}
|
||||
<Controller
|
||||
name="method"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText={`The method you would like to use to connect with ${
|
||||
APP_CONNECTION_MAP[AppConnection.Camunda].name
|
||||
}. This field cannot be changed after creation.`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Method"
|
||||
>
|
||||
<Select
|
||||
isDisabled={isUpdate}
|
||||
value={value}
|
||||
onValueChange={(val) => onChange(val)}
|
||||
className="w-full border border-mineshaft-500"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
{Object.values(CamundaConnectionMethod).map((method) => {
|
||||
return (
|
||||
<SelectItem value={method} key={method}>
|
||||
{getAppConnectionMethodDetails(method).name}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.clientId"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Client ID"
|
||||
>
|
||||
<Input {...field} placeholder="05810c8f-c44d-4bd0-a327-aab6dd77719b" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.clientSecret"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Client Secret"
|
||||
>
|
||||
<SecretInput
|
||||
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
colorSchema="secondary"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
>
|
||||
{isUpdate ? "Update Credentials" : "Connect to Camunda"}
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user