Address PR comment for terraform sync integration

This commit is contained in:
carlosmonastyrski
2025-04-11 16:23:07 -03:00
parent e19c3630d9
commit 4b0e5fa05b
91 changed files with 1463 additions and 40 deletions
+8
View File
@@ -1711,6 +1711,10 @@ export const AppConnections = {
}, },
VERCEL: { VERCEL: {
apiToken: "The API token used to authenticate with Vercel." apiToken: "The API token used to authenticate with Vercel."
},
CAMUNDA: {
clientId: "The client ID used to authenticate with Camunda.",
clientSecret: "The client secret used to authenticate with Camunda."
} }
} }
}; };
@@ -1821,6 +1825,10 @@ export const SecretSyncs = {
DATABRICKS: { DATABRICKS: {
scope: "The Databricks secret scope that secrets should be synced to." scope: "The Databricks secret scope that secrets should be synced to."
}, },
CAMUNDA: {
scope: "The Camunda scope that secrets should be synced to.",
clusterUUID: "The UUID of the Camunda cluster that secrets should be synced to."
},
HUMANITEC: { HUMANITEC: {
app: "The ID of the Humanitec app to sync secrets to.", app: "The ID of the Humanitec app to sync secrets to.",
org: "The ID of the Humanitec org to sync secrets to.", org: "The ID of the Humanitec org to sync secrets to.",
@@ -12,6 +12,10 @@ import {
AzureKeyVaultConnectionListItemSchema, AzureKeyVaultConnectionListItemSchema,
SanitizedAzureKeyVaultConnectionSchema SanitizedAzureKeyVaultConnectionSchema
} from "@app/services/app-connection/azure-key-vault"; } from "@app/services/app-connection/azure-key-vault";
import {
CamundaConnectionListItemSchema,
SanitizedCamundaConnectionSchema
} from "@app/services/app-connection/camunda";
import { import {
DatabricksConnectionListItemSchema, DatabricksConnectionListItemSchema,
SanitizedDatabricksConnectionSchema SanitizedDatabricksConnectionSchema
@@ -46,7 +50,8 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedTerraformCloudConnectionSchema.options, ...SanitizedTerraformCloudConnectionSchema.options,
...SanitizedVercelConnectionSchema.options, ...SanitizedVercelConnectionSchema.options,
...SanitizedPostgresConnectionSchema.options, ...SanitizedPostgresConnectionSchema.options,
...SanitizedMsSqlConnectionSchema.options ...SanitizedMsSqlConnectionSchema.options,
...SanitizedCamundaConnectionSchema.options
]); ]);
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
@@ -60,7 +65,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
TerraformCloudConnectionListItemSchema, TerraformCloudConnectionListItemSchema,
VercelConnectionListItemSchema, VercelConnectionListItemSchema,
PostgresConnectionListItemSchema, PostgresConnectionListItemSchema,
MsSqlConnectionListItemSchema MsSqlConnectionListItemSchema,
CamundaConnectionListItemSchema
]); ]);
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => { 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 { registerAwsConnectionRouter } from "./aws-connection-router";
import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router"; import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router";
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router"; import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
import { registerDatabricksConnectionRouter } from "./databricks-connection-router"; import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
import { registerGcpConnectionRouter } from "./gcp-connection-router"; import { registerGcpConnectionRouter } from "./gcp-connection-router";
import { registerGitHubConnectionRouter } from "./github-connection-router"; import { registerGitHubConnectionRouter } from "./github-connection-router";
@@ -26,5 +27,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.TerraformCloud]: registerTerraformCloudConnectionRouter, [AppConnection.TerraformCloud]: registerTerraformCloudConnectionRouter,
[AppConnection.Vercel]: registerVercelConnectionRouter, [AppConnection.Vercel]: registerVercelConnectionRouter,
[AppConnection.Postgres]: registerPostgresConnectionRouter, [AppConnection.Postgres]: registerPostgresConnectionRouter,
[AppConnection.MsSql]: registerMsSqlConnectionRouter [AppConnection.MsSql]: registerMsSqlConnectionRouter,
[AppConnection.Camunda]: registerCamundaConnectionRouter
}; };
@@ -0,0 +1,13 @@
import { CamundaSyncSchema, CreateCamundaSyncSchema, UpdateCamundaSyncSchema } from "@app/services/secret-sync/camunda";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
export const registerCamundaSyncRouter = async (server: FastifyZodProvider) =>
registerSyncSecretsEndpoints({
destination: SecretSync.Camunda,
server,
responseSchema: CamundaSyncSchema,
createSchema: CreateCamundaSyncSchema,
updateSchema: UpdateCamundaSyncSchema
});
@@ -4,6 +4,7 @@ import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-
import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router"; import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router";
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router"; import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router"; import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
import { registerCamundaSyncRouter } from "./camunda-sync-router";
import { registerDatabricksSyncRouter } from "./databricks-sync-router"; import { registerDatabricksSyncRouter } from "./databricks-sync-router";
import { registerGcpSyncRouter } from "./gcp-sync-router"; import { registerGcpSyncRouter } from "./gcp-sync-router";
import { registerGitHubSyncRouter } from "./github-sync-router"; import { registerGitHubSyncRouter } from "./github-sync-router";
@@ -23,5 +24,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
[SecretSync.Databricks]: registerDatabricksSyncRouter, [SecretSync.Databricks]: registerDatabricksSyncRouter,
[SecretSync.Humanitec]: registerHumanitecSyncRouter, [SecretSync.Humanitec]: registerHumanitecSyncRouter,
[SecretSync.TerraformCloud]: registerTerraformCloudSyncRouter, [SecretSync.TerraformCloud]: registerTerraformCloudSyncRouter,
[SecretSync.Camunda]: registerCamundaSyncRouter,
[SecretSync.Vercel]: registerVercelSyncRouter [SecretSync.Vercel]: registerVercelSyncRouter
}; };
@@ -18,6 +18,7 @@ import {
AzureAppConfigurationSyncSchema AzureAppConfigurationSyncSchema
} from "@app/services/secret-sync/azure-app-configuration"; } from "@app/services/secret-sync/azure-app-configuration";
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault"; import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks"; import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp"; import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github"; import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
@@ -35,6 +36,7 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
DatabricksSyncSchema, DatabricksSyncSchema,
HumanitecSyncSchema, HumanitecSyncSchema,
TerraformCloudSyncSchema, TerraformCloudSyncSchema,
CamundaSyncSchema,
VercelSyncSchema VercelSyncSchema
]); ]);
@@ -48,6 +50,7 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
DatabricksSyncListItemSchema, DatabricksSyncListItemSchema,
HumanitecSyncListItemSchema, HumanitecSyncListItemSchema,
TerraformCloudSyncListItemSchema, TerraformCloudSyncListItemSchema,
CamundaSyncListItemSchema,
VercelSyncListItemSchema VercelSyncListItemSchema
]); ]);
@@ -9,7 +9,8 @@ export enum AppConnection {
TerraformCloud = "terraform-cloud", TerraformCloud = "terraform-cloud",
Vercel = "vercel", Vercel = "vercel",
Postgres = "postgres", Postgres = "postgres",
MsSql = "mssql" MsSql = "mssql",
Camunda = "camunda"
} }
export enum AWSRegion { export enum AWSRegion {
@@ -27,6 +27,7 @@ import {
getAzureKeyVaultConnectionListItem, getAzureKeyVaultConnectionListItem,
validateAzureKeyVaultConnectionCredentials validateAzureKeyVaultConnectionCredentials
} from "./azure-key-vault"; } from "./azure-key-vault";
import { CamundaConnectionMethod, getCamundaConnectionListItem, validateCamundaConnectionCredentials } from "./camunda";
import { import {
DatabricksConnectionMethod, DatabricksConnectionMethod,
getDatabricksConnectionListItem, getDatabricksConnectionListItem,
@@ -61,7 +62,8 @@ export const listAppConnectionOptions = () => {
getTerraformCloudConnectionListItem(), getTerraformCloudConnectionListItem(),
getVercelConnectionListItem(), getVercelConnectionListItem(),
getPostgresConnectionListItem(), getPostgresConnectionListItem(),
getMsSqlConnectionListItem() getMsSqlConnectionListItem(),
getCamundaConnectionListItem()
].sort((a, b) => a.name.localeCompare(b.name)); ].sort((a, b) => a.name.localeCompare(b.name));
}; };
@@ -119,6 +121,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TAppConnect
[AppConnection.Postgres]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.Postgres]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.MsSql]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.TerraformCloud]: validateTerraformCloudConnectionCredentials as TAppConnectionCredentialsValidator, [AppConnection.TerraformCloud]: validateTerraformCloudConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Camunda]: validateCamundaConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Vercel]: validateVercelConnectionCredentials as TAppConnectionCredentialsValidator [AppConnection.Vercel]: validateVercelConnectionCredentials as TAppConnectionCredentialsValidator
}; };
@@ -142,6 +145,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
return "Service Account Impersonation"; return "Service Account Impersonation";
case DatabricksConnectionMethod.ServicePrincipal: case DatabricksConnectionMethod.ServicePrincipal:
return "Service Principal"; return "Service Principal";
case CamundaConnectionMethod.ClientCredentials:
return "Client Credentials";
case HumanitecConnectionMethod.ApiToken: case HumanitecConnectionMethod.ApiToken:
case TerraformCloudConnectionMethod.ApiToken: case TerraformCloudConnectionMethod.ApiToken:
case VercelConnectionMethod.ApiToken: case VercelConnectionMethod.ApiToken:
@@ -190,5 +195,6 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.Postgres]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform, [AppConnection.Postgres]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
[AppConnection.MsSql]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform, [AppConnection.MsSql]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
[AppConnection.TerraformCloud]: platformManagedCredentialsNotSupported, [AppConnection.TerraformCloud]: platformManagedCredentialsNotSupported,
[AppConnection.Camunda]: platformManagedCredentialsNotSupported,
[AppConnection.Vercel]: platformManagedCredentialsNotSupported [AppConnection.Vercel]: platformManagedCredentialsNotSupported
}; };
@@ -11,5 +11,6 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.TerraformCloud]: "Terraform Cloud", [AppConnection.TerraformCloud]: "Terraform Cloud",
[AppConnection.Vercel]: "Vercel", [AppConnection.Vercel]: "Vercel",
[AppConnection.Postgres]: "PostgreSQL", [AppConnection.Postgres]: "PostgreSQL",
[AppConnection.MsSql]: "Microsoft SQL Server" [AppConnection.MsSql]: "Microsoft SQL Server",
[AppConnection.Camunda]: "Camunda"
}; };
@@ -31,6 +31,8 @@ import { ValidateAwsConnectionCredentialsSchema } from "./aws";
import { awsConnectionService } from "./aws/aws-connection-service"; import { awsConnectionService } from "./aws/aws-connection-service";
import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration"; import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration";
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault"; import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
import { camundaConnectionService } from "./camunda/camunda-connection-service";
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks"; import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
import { databricksConnectionService } from "./databricks/databricks-connection-service"; import { databricksConnectionService } from "./databricks/databricks-connection-service";
import { ValidateGcpConnectionCredentialsSchema } from "./gcp"; import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
@@ -65,7 +67,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.TerraformCloud]: ValidateTerraformCloudConnectionCredentialsSchema, [AppConnection.TerraformCloud]: ValidateTerraformCloudConnectionCredentialsSchema,
[AppConnection.Vercel]: ValidateVercelConnectionCredentialsSchema, [AppConnection.Vercel]: ValidateVercelConnectionCredentialsSchema,
[AppConnection.Postgres]: ValidatePostgresConnectionCredentialsSchema, [AppConnection.Postgres]: ValidatePostgresConnectionCredentialsSchema,
[AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema [AppConnection.MsSql]: ValidateMsSqlConnectionCredentialsSchema,
[AppConnection.Camunda]: ValidateCamundaConnectionCredentialsSchema
}; };
export const appConnectionServiceFactory = ({ export const appConnectionServiceFactory = ({
@@ -438,6 +441,7 @@ export const appConnectionServiceFactory = ({
aws: awsConnectionService(connectAppConnectionById), aws: awsConnectionService(connectAppConnectionById),
humanitec: humanitecConnectionService(connectAppConnectionById), humanitec: humanitecConnectionService(connectAppConnectionById),
terraformCloud: terraformCloudConnectionService(connectAppConnectionById), terraformCloud: terraformCloudConnectionService(connectAppConnectionById),
camunda: camundaConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
vercel: vercelConnectionService(connectAppConnectionById) vercel: vercelConnectionService(connectAppConnectionById)
}; };
}; };
@@ -21,6 +21,12 @@ import {
TAzureKeyVaultConnectionInput, TAzureKeyVaultConnectionInput,
TValidateAzureKeyVaultConnectionCredentialsSchema TValidateAzureKeyVaultConnectionCredentialsSchema
} from "./azure-key-vault"; } from "./azure-key-vault";
import {
TCamundaConnection,
TCamundaConnectionConfig,
TCamundaConnectionInput,
TValidateCamundaConnectionCredentialsSchema
} from "./camunda";
import { import {
TDatabricksConnection, TDatabricksConnection,
TDatabricksConnectionConfig, TDatabricksConnectionConfig,
@@ -76,6 +82,7 @@ export type TAppConnection = { id: string } & (
| TVercelConnection | TVercelConnection
| TPostgresConnection | TPostgresConnection
| TMsSqlConnection | TMsSqlConnection
| TCamundaConnection
); );
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>; export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
@@ -94,6 +101,7 @@ export type TAppConnectionInput = { id: string } & (
| TVercelConnectionInput | TVercelConnectionInput
| TPostgresConnectionInput | TPostgresConnectionInput
| TMsSqlConnectionInput | TMsSqlConnectionInput
| TCamundaConnectionInput
); );
export type TSqlConnectionInput = TPostgresConnectionInput | TMsSqlConnectionInput; export type TSqlConnectionInput = TPostgresConnectionInput | TMsSqlConnectionInput;
@@ -117,7 +125,8 @@ export type TAppConnectionConfig =
| THumanitecConnectionConfig | THumanitecConnectionConfig
| TTerraformCloudConnectionConfig | TTerraformCloudConnectionConfig
| TVercelConnectionConfig | TVercelConnectionConfig
| TSqlConnectionConfig; | TSqlConnectionConfig
| TCamundaConnectionConfig;
export type TValidateAppConnectionCredentialsSchema = export type TValidateAppConnectionCredentialsSchema =
| TValidateAwsConnectionCredentialsSchema | TValidateAwsConnectionCredentialsSchema
@@ -130,6 +139,7 @@ export type TValidateAppConnectionCredentialsSchema =
| TValidatePostgresConnectionCredentialsSchema | TValidatePostgresConnectionCredentialsSchema
| TValidateMsSqlConnectionCredentialsSchema | TValidateMsSqlConnectionCredentialsSchema
| TValidateTerraformCloudConnectionCredentialsSchema | TValidateTerraformCloudConnectionCredentialsSchema
| TValidateCamundaConnectionCredentialsSchema
| TValidateVercelConnectionCredentialsSchema; | TValidateVercelConnectionCredentialsSchema;
export type TListAwsConnectionKmsKeys = { export type TListAwsConnectionKmsKeys = {
@@ -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 30 seconds from expiry
if (Date.now() < expiresAt - 30_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,77 @@
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").describe(AppConnections.CREDENTIALS.CAMUNDA.clientId),
clientSecret: z
.string()
.trim()
.min(1, "Client Secret required")
.describe(AppConnections.CREDENTIALS.CAMUNDA.clientSecret)
});
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 }[];
@@ -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", GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token",
GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token", GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token",
BITBUCKET_TOKEN_URL = "https://bitbucket.org/site/oauth2/access_token", BITBUCKET_TOKEN_URL = "https://bitbucket.org/site/oauth2/access_token",
CAMUNDA_TOKEN_URL = "https://login.cloud.camunda.io/oauth/token",
// integration apps endpoints // integration apps endpoints
GCP_API_URL = "https://cloudresourcemanager.googleapis.com", 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", HASURA_CLOUD_API_URL = "https://data.pro.hasura.io/v1/graphql",
AZURE_DEVOPS_API_URL = "https://dev.azure.com", AZURE_DEVOPS_API_URL = "https://dev.azure.com",
HUMANITEC_API_URL = "https://api.humanitec.io", 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_SERVICE_NAME = "secretmanager.googleapis.com",
GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`, GCP_SECRET_MANAGER_URL = `https://${GCP_SECRET_MANAGER_SERVICE_NAME}`,
@@ -0,0 +1,10 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
export const CAMUNDA_SYNC_LIST_OPTION: TSecretSyncListItem = {
name: "Camunda",
destination: SecretSync.Camunda,
connection: AppConnection.Camunda,
canImportSecrets: true
};
@@ -0,0 +1,173 @@
import { request } from "@app/lib/config/request";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
import { getCamundaConnectionAccessToken } from "@app/services/app-connection/camunda";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import {
TCamundaCreateSecret,
TCamundaDeleteSecret,
TCamundaListSecrets,
TCamundaListSecretsResponse,
TCamundaPutSecret,
TCamundaSyncWithCredentials
} from "@app/services/secret-sync/camunda/camunda-sync-types";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
import { TSecretMap } from "../secret-sync-types";
type TCamundaSecretSyncFactoryDeps = {
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
};
const getCamundaSecrets = async ({ accessToken, clusterUUID }: TCamundaListSecrets) => {
const { data } = await request.get<TCamundaListSecretsResponse>(
`${IntegrationUrls.CAMUNDA_API_URL}/clusters/${clusterUUID}/secrets`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
}
);
return data;
};
const createCamundaSecret = async ({ accessToken, clusterUUID, key, value }: TCamundaCreateSecret) =>
request.post(
`${IntegrationUrls.CAMUNDA_API_URL}/clusters/${clusterUUID}/secrets`,
{
secretName: key,
secretValue: value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
}
);
const deleteCamundaSecret = async ({ accessToken, clusterUUID, key }: TCamundaDeleteSecret) =>
request.delete(`${IntegrationUrls.CAMUNDA_API_URL}/clusters/${clusterUUID}/secrets/${key}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
});
const updateCamundaSecret = async ({ accessToken, clusterUUID, key, value }: TCamundaPutSecret) =>
request.put(
`${IntegrationUrls.CAMUNDA_API_URL}/clusters/${clusterUUID}/secrets/${key}`,
{
secretValue: value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
}
);
export const camundaSyncFactory = ({ kmsService, appConnectionDAL }: TCamundaSecretSyncFactoryDeps) => {
const syncSecrets = async (secretSync: TCamundaSyncWithCredentials, secretMap: TSecretMap) => {
const {
destinationConfig: { clusterUUID },
connection
} = secretSync;
const accessToken = await getCamundaConnectionAccessToken(connection, appConnectionDAL, kmsService);
const camundaSecrets = await getCamundaSecrets({ accessToken, clusterUUID });
for await (const entry of Object.entries(secretMap)) {
const [key, { value }] = entry;
if (!value) {
// eslint-disable-next-line no-continue
continue;
}
try {
if (camundaSecrets[key] === undefined) {
await createCamundaSecret({
key,
value,
clusterUUID,
accessToken
});
} else if (camundaSecrets[key] !== value) {
await updateCamundaSecret({
key,
value,
clusterUUID,
accessToken
});
}
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
}
if (secretSync.syncOptions.disableSecretDeletion) return;
for await (const secret of Object.keys(camundaSecrets)) {
if (!(secret in secretMap) || !secretMap[secret].value) {
try {
await deleteCamundaSecret({
key: secret,
clusterUUID,
accessToken
});
} catch (error) {
throw new SecretSyncError({
error,
secretKey: secret
});
}
}
}
};
const removeSecrets = async (secretSync: TCamundaSyncWithCredentials, secretMap: TSecretMap) => {
const {
destinationConfig: { clusterUUID },
connection
} = secretSync;
const accessToken = await getCamundaConnectionAccessToken(connection, appConnectionDAL, kmsService);
const camundaSecrets = await getCamundaSecrets({ accessToken, clusterUUID });
for await (const secret of Object.keys(camundaSecrets)) {
if (!(secret in secretMap)) {
await deleteCamundaSecret({
key: secret,
clusterUUID,
accessToken
});
}
}
};
const getSecrets = async (secretSync: TCamundaSyncWithCredentials) => {
const {
destinationConfig: { clusterUUID },
connection
} = secretSync;
const accessToken = await getCamundaConnectionAccessToken(connection, appConnectionDAL, kmsService);
const camundaSecrets = await getCamundaSecrets({ accessToken, clusterUUID });
return Object.fromEntries(Object.entries(camundaSecrets).map(([key, value]) => [key, { value }]));
};
return {
syncSecrets,
removeSecrets,
getSecrets
};
};
@@ -0,0 +1,47 @@
import { z } from "zod";
import { SecretSyncs } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import {
BaseSecretSyncSchema,
GenericCreateSecretSyncFieldsSchema,
GenericUpdateSecretSyncFieldsSchema
} from "@app/services/secret-sync/secret-sync-schemas";
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
const CamundaSyncDestinationConfigSchema = z.object({
scope: z.string().trim().min(1, "Camunda scope required").describe(SecretSyncs.DESTINATION_CONFIG.CAMUNDA.scope),
clusterUUID: z
.string()
.min(1, "Camunda cluster UUID is required")
.describe(SecretSyncs.DESTINATION_CONFIG.CAMUNDA.clusterUUID)
});
const CamundaSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
export const CamundaSyncSchema = BaseSecretSyncSchema(SecretSync.Camunda, CamundaSyncOptionsConfig).extend({
destination: z.literal(SecretSync.Camunda),
destinationConfig: CamundaSyncDestinationConfigSchema
});
export const CreateCamundaSyncSchema = GenericCreateSecretSyncFieldsSchema(
SecretSync.Camunda,
CamundaSyncOptionsConfig
).extend({
destinationConfig: CamundaSyncDestinationConfigSchema
});
export const UpdateCamundaSyncSchema = GenericUpdateSecretSyncFieldsSchema(
SecretSync.Camunda,
CamundaSyncOptionsConfig
).extend({
destinationConfig: CamundaSyncDestinationConfigSchema.optional()
});
export const CamundaSyncListItemSchema = z.object({
name: z.literal("Camunda"),
connection: z.literal(AppConnection.Camunda),
destination: z.literal(SecretSync.Camunda),
canImportSecrets: z.literal(true)
});
@@ -0,0 +1,38 @@
import { z } from "zod";
import { TCamundaConnection } from "@app/services/app-connection/camunda";
import { CamundaSyncListItemSchema, CamundaSyncSchema, CreateCamundaSyncSchema } from "./camunda-sync-schemas";
export type TCamundaSync = z.infer<typeof CamundaSyncSchema>;
export type TCamundaSyncInput = z.infer<typeof CreateCamundaSyncSchema>;
export type TCamundaSyncListItem = z.infer<typeof CamundaSyncListItemSchema>;
export type TCamundaSyncWithCredentials = TCamundaSync & {
connection: TCamundaConnection;
};
export type TCamundaListSecretsResponse = { [key: string]: string };
type TBaseCamundaSecretRequest = {
accessToken: string;
clusterUUID: string;
};
export type TCamundaListSecrets = TBaseCamundaSecretRequest;
export type TCamundaCreateSecret = {
key: string;
value?: string;
} & TBaseCamundaSecretRequest;
export type TCamundaPutSecret = {
key: string;
value?: string;
} & TBaseCamundaSecretRequest;
export type TCamundaDeleteSecret = {
key: string;
} & TBaseCamundaSecretRequest;
@@ -0,0 +1,4 @@
export * from "./camunda-sync-constants";
export * from "./camunda-sync-fns";
export * from "./camunda-sync-schemas";
export * from "./camunda-sync-types";
@@ -8,6 +8,7 @@ export enum SecretSync {
Databricks = "databricks", Databricks = "databricks",
Humanitec = "humanitec", Humanitec = "humanitec",
TerraformCloud = "terraform-cloud", TerraformCloud = "terraform-cloud",
Camunda = "camunda",
Vercel = "vercel" Vercel = "vercel"
} }
@@ -22,6 +22,7 @@ import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFactory } from "./azure-app-configuration"; import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFactory } from "./azure-app-configuration";
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault"; import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
import { GCP_SYNC_LIST_OPTION } from "./gcp"; import { GCP_SYNC_LIST_OPTION } from "./gcp";
import { GcpSyncFns } from "./gcp/gcp-sync-fns"; import { GcpSyncFns } from "./gcp/gcp-sync-fns";
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec"; import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
@@ -39,6 +40,7 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
[SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION, [SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION,
[SecretSync.Humanitec]: HUMANITEC_SYNC_LIST_OPTION, [SecretSync.Humanitec]: HUMANITEC_SYNC_LIST_OPTION,
[SecretSync.TerraformCloud]: TERRAFORM_CLOUD_SYNC_LIST_OPTION, [SecretSync.TerraformCloud]: TERRAFORM_CLOUD_SYNC_LIST_OPTION,
[SecretSync.Camunda]: CAMUNDA_SYNC_LIST_OPTION,
[SecretSync.Vercel]: VERCEL_SYNC_LIST_OPTION [SecretSync.Vercel]: VERCEL_SYNC_LIST_OPTION
}; };
@@ -127,6 +129,11 @@ export const SecretSyncFns = {
return HumanitecSyncFns.syncSecrets(secretSync, secretMap); return HumanitecSyncFns.syncSecrets(secretSync, secretMap);
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
return TerraformCloudSyncFns.syncSecrets(secretSync, secretMap); return TerraformCloudSyncFns.syncSecrets(secretSync, secretMap);
case SecretSync.Camunda:
return camundaSyncFactory({
appConnectionDAL,
kmsService
}).syncSecrets(secretSync, secretMap);
case SecretSync.Vercel: case SecretSync.Vercel:
return VercelSyncFns.syncSecrets(secretSync, secretMap); return VercelSyncFns.syncSecrets(secretSync, secretMap);
default: default:
@@ -176,6 +183,12 @@ export const SecretSyncFns = {
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
secretMap = await TerraformCloudSyncFns.getSecrets(secretSync); secretMap = await TerraformCloudSyncFns.getSecrets(secretSync);
break; break;
case SecretSync.Camunda:
secretMap = await camundaSyncFactory({
appConnectionDAL,
kmsService
}).getSecrets(secretSync);
break;
case SecretSync.Vercel: case SecretSync.Vercel:
secretMap = await VercelSyncFns.getSecrets(secretSync); secretMap = await VercelSyncFns.getSecrets(secretSync);
break; break;
@@ -223,6 +236,11 @@ export const SecretSyncFns = {
return HumanitecSyncFns.removeSecrets(secretSync, secretMap); return HumanitecSyncFns.removeSecrets(secretSync, secretMap);
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
return TerraformCloudSyncFns.removeSecrets(secretSync, secretMap); return TerraformCloudSyncFns.removeSecrets(secretSync, secretMap);
case SecretSync.Camunda:
return camundaSyncFactory({
appConnectionDAL,
kmsService
}).removeSecrets(secretSync, secretMap);
case SecretSync.Vercel: case SecretSync.Vercel:
return VercelSyncFns.removeSecrets(secretSync, secretMap); return VercelSyncFns.removeSecrets(secretSync, secretMap);
default: default:
@@ -11,6 +11,7 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
[SecretSync.Databricks]: "Databricks", [SecretSync.Databricks]: "Databricks",
[SecretSync.Humanitec]: "Humanitec", [SecretSync.Humanitec]: "Humanitec",
[SecretSync.TerraformCloud]: "Terraform Cloud", [SecretSync.TerraformCloud]: "Terraform Cloud",
[SecretSync.Camunda]: "Camunda",
[SecretSync.Vercel]: "Vercel" [SecretSync.Vercel]: "Vercel"
}; };
@@ -24,5 +25,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Databricks]: AppConnection.Databricks, [SecretSync.Databricks]: AppConnection.Databricks,
[SecretSync.Humanitec]: AppConnection.Humanitec, [SecretSync.Humanitec]: AppConnection.Humanitec,
[SecretSync.TerraformCloud]: AppConnection.TerraformCloud, [SecretSync.TerraformCloud]: AppConnection.TerraformCloud,
[SecretSync.Camunda]: AppConnection.Camunda,
[SecretSync.Vercel]: AppConnection.Vercel [SecretSync.Vercel]: AppConnection.Vercel
}; };
@@ -9,6 +9,12 @@ import {
TAwsSecretsManagerSyncListItem, TAwsSecretsManagerSyncListItem,
TAwsSecretsManagerSyncWithCredentials TAwsSecretsManagerSyncWithCredentials
} from "@app/services/secret-sync/aws-secrets-manager"; } from "@app/services/secret-sync/aws-secrets-manager";
import {
TCamundaSync,
TCamundaSyncInput,
TCamundaSyncListItem,
TCamundaSyncWithCredentials
} from "@app/services/secret-sync/camunda";
import { import {
TDatabricksSync, TDatabricksSync,
TDatabricksSyncInput, TDatabricksSyncInput,
@@ -67,6 +73,7 @@ export type TSecretSync =
| TDatabricksSync | TDatabricksSync
| THumanitecSync | THumanitecSync
| TTerraformCloudSync | TTerraformCloudSync
| TCamundaSync
| TVercelSync; | TVercelSync;
export type TSecretSyncWithCredentials = export type TSecretSyncWithCredentials =
@@ -79,6 +86,7 @@ export type TSecretSyncWithCredentials =
| TDatabricksSyncWithCredentials | TDatabricksSyncWithCredentials
| THumanitecSyncWithCredentials | THumanitecSyncWithCredentials
| TTerraformCloudSyncWithCredentials | TTerraformCloudSyncWithCredentials
| TCamundaSyncWithCredentials
| TVercelSyncWithCredentials; | TVercelSyncWithCredentials;
export type TSecretSyncInput = export type TSecretSyncInput =
@@ -91,6 +99,7 @@ export type TSecretSyncInput =
| TDatabricksSyncInput | TDatabricksSyncInput
| THumanitecSyncInput | THumanitecSyncInput
| TTerraformCloudSyncInput | TTerraformCloudSyncInput
| TCamundaSyncInput
| TVercelSyncInput; | TVercelSyncInput;
export type TSecretSyncListItem = export type TSecretSyncListItem =
@@ -103,6 +112,7 @@ export type TSecretSyncListItem =
| TDatabricksSyncListItem | TDatabricksSyncListItem
| THumanitecSyncListItem | THumanitecSyncListItem
| TTerraformCloudSyncListItem | TTerraformCloudSyncListItem
| TCamundaSyncListItem
| TVercelSyncListItem; | TVercelSyncListItem;
export type TSyncOptionsConfig = { export type TSyncOptionsConfig = {
@@ -0,0 +1,4 @@
---
title: "Available"
openapi: "GET /api/v1/app-connections/camunda/available"
---
@@ -0,0 +1,4 @@
---
title: "Create"
openapi: "POST /api/v1/app-connections/camunda"
---
@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/app-connections/camunda/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/app-connections/camunda/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/app-connections/camunda/connection-name/{connectionName}"
---
@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/app-connections/camunda"
---
@@ -0,0 +1,4 @@
---
title: "Update"
openapi: "PATCH /api/v1/app-connections/camunda/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Create"
openapi: "POST /api/v1/secret-syncs/camunda"
---
@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/secret-syncs/camunda/{syncId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/secret-syncs/camunda/{syncId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/secret-syncs/camunda/sync-name/{syncName}"
---
@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/secret-syncs/camunda"
---
@@ -0,0 +1,4 @@
---
title: "Remove Secrets"
openapi: "POST /api/v1/secret-syncs/camunda/{syncId}/remove-secrets"
---
@@ -0,0 +1,4 @@
---
title: "Sync Secrets"
openapi: "POST /api/v1/secret-syncs/camunda/{syncId}/sync-secrets"
---
@@ -0,0 +1,4 @@
---
title: "Update"
openapi: "PATCH /api/v1/secret-syncs/camunda/{syncId}"
---
@@ -1,4 +1,4 @@
--- ---
title: "Create" title: "Create"
openapi: "POST /api/v1/secret-syncs/humanitec" openapi: "POST /api/v1/secret-syncs/terraform-cloud"
--- ---
Binary file not shown.

After

Width:  |  Height:  |  Size: 964 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

@@ -0,0 +1,77 @@
---
title: "Camunda Connection"
description: "Learn how to configure a Camunda Connection for Infisical."
---
Infisical supports connecting to Camunda APIs using [client credentials](https://docs.camunda.io/docs/apis-tools/administration-api/authentication/#client-credentials-and-scopes).
## Configure Client Credentials for Infisical
<Steps>
<Step title="Navigate to Organization Management">
In your Camunda Cloud Console, navigate to the **Organization** tab in the top navigation menu.
![Organization Management](/images/app-connections/camunda/camunda-console.png)
</Step>
<Step title="Access Administration API">
From the Organization Management tabs, click on **Administration API** to manage your API credentials and click the **Create client credentials** button.
![Create Client Credentials](/images/app-connections/camunda/camunda-organization-page.png)
</Step>
<Step title="Name Your Client">
Enter a recognizable name for your client, such as "my-infisical-client". The name can contain letters, dashes, underscores, and digits.
</Step>
<Step title="Set Credential Permissions">
In the "Create new client credentials" modal, select the following permissions required for secret syncs:
- **Cluster**: Enable read access (Get)
- **Connector secrets**: Enable all operations (Get, Create, Update, Delete)
These specific permissions are required for Infisical to properly sync and manage your Camunda secrets.
![Set Permissions](/images/app-connections/camunda/camunda-create-client-1.png)
![Set Permissions 2](/images/app-connections/camunda/camunda-create-client-2.png)
</Step>
<Step title="Complete Creation">
Click the **Create** button to generate your client credentials.
</Step>
<Step title="Save Your Credentials">
After creation, you'll be shown your client credentials. For the Infisical connection, you'll need:
- **Client ID** (`CAMUNDA_CONSOLE_CLIENT_ID`)
- **Client Secret** (`CAMUNDA_CONSOLE_CLIENT_SECRET`)
**IMPORTANT**: Make sure to securely save the Client Secret, as it will not be shown again after you close this dialog.
You can download these credentials or copy them to use in the next section.
![Client Credentials](/images/app-connections/camunda/camunda-client-credentials.png)
</Step>
</Steps>
## Setup Camunda Connection in Infisical
<Steps>
<Step title="Navigate to App Connections">
Navigate to the **App Connections** tab on the **Organization Settings**
page. ![App Connections
Tab](/images/app-connections/general/add-connection.png)
</Step>
<Step title="Add Connection">
Select the **Camunda Connection** option from the connection options modal.
![Select Camunda
Connection](/images/app-connections/camunda/camunda-app-connection-select.png)
</Step>
<Step title="Input Credentials">
Select the **Client Credentials** method and enter the Camunda client
credentials you created:
- **Client ID**: Your `CAMUNDA_CONSOLE_CLIENT_ID` value
- **Client Secret**: Your `CAMUNDA_CONSOLE_CLIENT_SECRET` value
Infisical will automatically configure the connection using these credentials to access the Camunda API. Click **Connect to Camunda** to establish the connection. ![Connect to Camunda](/images/app-connections/camunda/camunda-app-connection-form.png)
</Step>
<Step title="Connection Created">
Your **Camunda Connection** is now available for use in your Infisical
projects. ![Camunda Connection
Created](/images/app-connections/camunda/camunda-app-connection-created.png)
</Step>
</Steps>
+139
View File
@@ -0,0 +1,139 @@
---
title: "Camunda Sync"
description: "Learn how to configure a Camunda Sync for Infisical."
---
**Prerequisites:**
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
- Create a [Camunda Connection](/integrations/app-connections/camunda)
<Tabs>
<Tab title="Infisical UI">
1. Navigate to **Project** > **Integrations** and select the **Secret Syncs** tab. Click on the **Add Sync** button.
![Secret Syncs Tab](/images/secret-syncs/general/secret-sync-tab.png)
2. Select the **Camunda** option.
![Select Camunda](/images/secret-syncs/camunda/select-camunda-option.png)
3. Configure the **Source** from where secrets should be retrieved, then click **Next**.
![Configure Source](/images/secret-syncs/camunda/camunda-source.png)
- **Environment**: The project environment to retrieve secrets from.
- **Secret Path**: The folder path to retrieve secrets from.
<Tip>
If you need to sync secrets from multiple folder locations, check out [secret imports](/documentation/platform/secret-reference#secret-imports).
</Tip>
4. Configure the **Destination** to where secrets should be deployed, then click **Next**.
![Configure Destination](/images/secret-syncs/camunda/camunda-destination.png)
- **Camunda Connection**: The Camunda Connection to authenticate with.
- **Cluster**: The Camunda cluster to sync connector secrets to.
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
![Configure Options](/images/secret-syncs/camunda/camunda-options.png)
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
- **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over Camunda when keys conflict.
- **Import Secrets (Prioritize Camunda)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Camunda over Infisical when keys conflict.
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
6. Configure the **Details** of your Camunda Sync, then click **Next**.
![Configure Details](/images/secret-syncs/camunda/camunda-details.png)
- **Name**: The name of your sync. Must be slug-friendly.
- **Description**: An optional description for your sync.
7. Review your Camunda Sync configuration, then click **Create Sync**.
![Confirm Configuration](/images/secret-syncs/camunda/camunda-review.png)
8. If enabled, your Camunda Sync will begin syncing your secrets to the destination endpoint.
![Sync Secrets](/images/secret-syncs/camunda/camunda-created.png)
</Tab>
<Tab title="API">
To create an **Camunda Sync**, make an API request to the [Create Camunda Sync](/api-reference/endpoints/secret-syncs/camunda/create) API endpoint.
### Sample request
```bash Request
curl --request POST \
--url https://app.infisical.com/api/v1/secret-syncs/camunda \
--header 'Content-Type: application/json' \
--data '{
"name": "my-camunda-sync",
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"description": "an example sync",
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"environment": "dev",
"secretPath": "/my-secrets",
"isEnabled": true,
"syncOptions": {
"initialSyncBehavior": "overwrite-destination"
},
"destinationConfig": {
"scope": "cluster",
"clusterUUID": "cc4c8dae-dce9-4f4c-9882-132b2bd65fa5"
}
}'
```
### Sample response
```bash Response
{
"secretSync": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "my-camunda-sync",
"description": "an example sync",
"isEnabled": true,
"version": 1,
"folderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"syncStatus": "succeeded",
"lastSyncJobId": "123",
"lastSyncMessage": null,
"lastSyncedAt": "2023-11-07T05:31:56Z",
"importStatus": null,
"lastImportJobId": null,
"lastImportMessage": null,
"lastImportedAt": null,
"removeStatus": null,
"lastRemoveJobId": null,
"lastRemoveMessage": null,
"lastRemovedAt": null,
"syncOptions": {
"initialSyncBehavior": "overwrite-destination"
},
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"connection": {
"app": "camunda",
"name": "my-camunda-connection",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"environment": {
"slug": "dev",
"name": "Development",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"folder": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"path": "/my-secrets"
},
"destination": "camunda",
"destinationConfig": {
"scope": "cluster",
"clusterUUID": "cc4c8dae-dce9-4f4c-9882-132b2bd65fa5"
}
}
}
```
</Tab>
</Tabs>
+54 -27
View File
@@ -417,14 +417,15 @@
"integrations/app-connections/aws", "integrations/app-connections/aws",
"integrations/app-connections/azure-app-configuration", "integrations/app-connections/azure-app-configuration",
"integrations/app-connections/azure-key-vault", "integrations/app-connections/azure-key-vault",
"integrations/app-connections/camunda",
"integrations/app-connections/databricks", "integrations/app-connections/databricks",
"integrations/app-connections/gcp", "integrations/app-connections/gcp",
"integrations/app-connections/github", "integrations/app-connections/github",
"integrations/app-connections/humanitec", "integrations/app-connections/humanitec",
"integrations/app-connections/terraform-cloud",
"integrations/app-connections/vercel",
"integrations/app-connections/mssql", "integrations/app-connections/mssql",
"integrations/app-connections/postgres" "integrations/app-connections/postgres",
"integrations/app-connections/terraform-cloud",
"integrations/app-connections/vercel"
] ]
} }
] ]
@@ -440,6 +441,7 @@
"integrations/secret-syncs/aws-secrets-manager", "integrations/secret-syncs/aws-secrets-manager",
"integrations/secret-syncs/azure-app-configuration", "integrations/secret-syncs/azure-app-configuration",
"integrations/secret-syncs/azure-key-vault", "integrations/secret-syncs/azure-key-vault",
"integrations/secret-syncs/camunda",
"integrations/secret-syncs/databricks", "integrations/secret-syncs/databricks",
"integrations/secret-syncs/gcp-secret-manager", "integrations/secret-syncs/gcp-secret-manager",
"integrations/secret-syncs/github", "integrations/secret-syncs/github",
@@ -916,6 +918,18 @@
"api-reference/endpoints/app-connections/azure-key-vault/delete" "api-reference/endpoints/app-connections/azure-key-vault/delete"
] ]
}, },
{
"group": "Camunda",
"pages": [
"api-reference/endpoints/app-connections/camunda/list",
"api-reference/endpoints/app-connections/camunda/available",
"api-reference/endpoints/app-connections/camunda/get-by-id",
"api-reference/endpoints/app-connections/camunda/get-by-name",
"api-reference/endpoints/app-connections/camunda/create",
"api-reference/endpoints/app-connections/camunda/update",
"api-reference/endpoints/app-connections/camunda/delete"
]
},
{ {
"group": "Databricks", "group": "Databricks",
"pages": [ "pages": [
@@ -964,30 +978,6 @@
"api-reference/endpoints/app-connections/humanitec/delete" "api-reference/endpoints/app-connections/humanitec/delete"
] ]
}, },
{
"group": "Terraform Cloud",
"pages": [
"api-reference/endpoints/app-connections/terraform-cloud/list",
"api-reference/endpoints/app-connections/terraform-cloud/available",
"api-reference/endpoints/app-connections/terraform-cloud/get-by-id",
"api-reference/endpoints/app-connections/terraform-cloud/get-by-name",
"api-reference/endpoints/app-connections/terraform-cloud/create",
"api-reference/endpoints/app-connections/terraform-cloud/update",
"api-reference/endpoints/app-connections/terraform-cloud/delete"
]
},
{
"group": "Vercel",
"pages": [
"api-reference/endpoints/app-connections/vercel/list",
"api-reference/endpoints/app-connections/vercel/available",
"api-reference/endpoints/app-connections/vercel/get-by-id",
"api-reference/endpoints/app-connections/vercel/get-by-name",
"api-reference/endpoints/app-connections/vercel/create",
"api-reference/endpoints/app-connections/vercel/update",
"api-reference/endpoints/app-connections/vercel/delete"
]
},
{ {
"group": "Microsoft SQL Server", "group": "Microsoft SQL Server",
"pages": [ "pages": [
@@ -1011,6 +1001,30 @@
"api-reference/endpoints/app-connections/postgres/update", "api-reference/endpoints/app-connections/postgres/update",
"api-reference/endpoints/app-connections/postgres/delete" "api-reference/endpoints/app-connections/postgres/delete"
] ]
},
{
"group": "Terraform Cloud",
"pages": [
"api-reference/endpoints/app-connections/terraform-cloud/list",
"api-reference/endpoints/app-connections/terraform-cloud/available",
"api-reference/endpoints/app-connections/terraform-cloud/get-by-id",
"api-reference/endpoints/app-connections/terraform-cloud/get-by-name",
"api-reference/endpoints/app-connections/terraform-cloud/create",
"api-reference/endpoints/app-connections/terraform-cloud/update",
"api-reference/endpoints/app-connections/terraform-cloud/delete"
]
},
{
"group": "Vercel",
"pages": [
"api-reference/endpoints/app-connections/vercel/list",
"api-reference/endpoints/app-connections/vercel/available",
"api-reference/endpoints/app-connections/vercel/get-by-id",
"api-reference/endpoints/app-connections/vercel/get-by-name",
"api-reference/endpoints/app-connections/vercel/create",
"api-reference/endpoints/app-connections/vercel/update",
"api-reference/endpoints/app-connections/vercel/delete"
]
} }
] ]
}, },
@@ -1075,6 +1089,19 @@
"api-reference/endpoints/secret-syncs/azure-key-vault/remove-secrets" "api-reference/endpoints/secret-syncs/azure-key-vault/remove-secrets"
] ]
}, },
{
"group": "Camunda",
"pages": [
"api-reference/endpoints/secret-syncs/camunda/list",
"api-reference/endpoints/secret-syncs/camunda/get-by-id",
"api-reference/endpoints/secret-syncs/camunda/get-by-name",
"api-reference/endpoints/secret-syncs/camunda/create",
"api-reference/endpoints/secret-syncs/camunda/update",
"api-reference/endpoints/secret-syncs/camunda/delete",
"api-reference/endpoints/secret-syncs/camunda/sync-secrets",
"api-reference/endpoints/secret-syncs/camunda/remove-secrets"
]
},
{ {
"group": "Databricks", "group": "Databricks",
"pages": [ "pages": [
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,80 @@
import { useEffect } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { SingleValue } from "react-select";
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
import { FilterableSelect, FormControl, Tooltip } from "@app/components/v2";
import { useCamundaConnectionListClusters } from "@app/hooks/api/appConnections/camunda";
import { TCamundaCluster } from "@app/hooks/api/appConnections/camunda/types";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { CamundaSyncScope } from "@app/hooks/api/secretSyncs/types/camunda-sync";
import { TSecretSyncForm } from "../schemas";
export const CamundaSyncFields = () => {
const { control, setValue } = useFormContext<
TSecretSyncForm & { destination: SecretSync.Camunda }
>();
const connectionId = useWatch({ name: "connection.id", control });
const { data: clusters, isPending } = useCamundaConnectionListClusters(connectionId, {
enabled: Boolean(connectionId)
});
useEffect(() => {
setValue("destinationConfig.scope", CamundaSyncScope.Cluster);
}, []);
return (
<>
<SecretSyncConnectionField
onChange={() => {
setValue("destinationConfig.clusterUUID", "");
}}
/>
<Controller
name="destinationConfig.clusterUUID"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
errorText={error?.message}
label="Cluster"
helperText={
<Tooltip
className="max-w-md"
content="Ensure that your credential has been granted access to the cluster"
>
<div>
<span>Don&#39;t see the cluster you&#39;re looking for?</span>{" "}
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
</div>
</Tooltip>
}
>
<FilterableSelect
menuPlacement="top"
isLoading={isPending && Boolean(connectionId)}
isDisabled={!connectionId}
value={clusters?.find((cluster) => cluster.uuid === value) ?? null}
onChange={(option) => {
onChange((option as SingleValue<TCamundaCluster>)?.uuid ?? null);
setValue(
"destinationConfig.clusterName",
(option as SingleValue<TCamundaCluster>)?.name ?? ""
);
}}
options={clusters}
placeholder="Select a cluster..."
getOptionLabel={(option) => option.name}
getOptionValue={(option) => option.uuid}
/>
</FormControl>
)}
/>
</>
);
};
@@ -7,6 +7,7 @@ import { AwsParameterStoreSyncFields } from "./AwsParameterStoreSyncFields";
import { AwsSecretsManagerSyncFields } from "./AwsSecretsManagerSyncFields"; import { AwsSecretsManagerSyncFields } from "./AwsSecretsManagerSyncFields";
import { AzureAppConfigurationSyncFields } from "./AzureAppConfigurationSyncFields"; import { AzureAppConfigurationSyncFields } from "./AzureAppConfigurationSyncFields";
import { AzureKeyVaultSyncFields } from "./AzureKeyVaultSyncFields"; import { AzureKeyVaultSyncFields } from "./AzureKeyVaultSyncFields";
import { CamundaSyncFields } from "./CamundaSyncFields";
import { DatabricksSyncFields } from "./DatabricksSyncFields"; import { DatabricksSyncFields } from "./DatabricksSyncFields";
import { GcpSyncFields } from "./GcpSyncFields"; import { GcpSyncFields } from "./GcpSyncFields";
import { GitHubSyncFields } from "./GitHubSyncFields"; import { GitHubSyncFields } from "./GitHubSyncFields";
@@ -38,6 +39,8 @@ export const SecretSyncDestinationFields = () => {
return <HumanitecSyncFields />; return <HumanitecSyncFields />;
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
return <TerraformCloudSyncFields />; return <TerraformCloudSyncFields />;
case SecretSync.Camunda:
return <CamundaSyncFields />;
case SecretSync.Vercel: case SecretSync.Vercel:
return <VercelSyncFields />; return <VercelSyncFields />;
default: default:
@@ -40,6 +40,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.Databricks: case SecretSync.Databricks:
case SecretSync.Humanitec: case SecretSync.Humanitec:
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
case SecretSync.Camunda:
case SecretSync.Vercel: case SecretSync.Vercel:
AdditionalSyncOptionsFieldsComponent = null; AdditionalSyncOptionsFieldsComponent = null;
break; break;
@@ -0,0 +1,20 @@
import { useFormContext } from "react-hook-form";
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const CamundaSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Camunda }>();
const scope = watch("destinationConfig.scope");
const clusterName = watch("destinationConfig.clusterName");
const clusterUUID = watch("destinationConfig.clusterUUID");
return (
<>
<GenericFieldLabel label="Secret Scope">{scope}</GenericFieldLabel>
<GenericFieldLabel label="Cluster">
{clusterName} (id:{clusterUUID})
</GenericFieldLabel>
</>
);
};
@@ -17,6 +17,7 @@ import {
} from "./AwsSecretsManagerSyncReviewFields"; } from "./AwsSecretsManagerSyncReviewFields";
import { AzureAppConfigurationSyncReviewFields } from "./AzureAppConfigurationSyncReviewFields"; import { AzureAppConfigurationSyncReviewFields } from "./AzureAppConfigurationSyncReviewFields";
import { AzureKeyVaultSyncReviewFields } from "./AzureKeyVaultSyncReviewFields"; import { AzureKeyVaultSyncReviewFields } from "./AzureKeyVaultSyncReviewFields";
import { CamundaSyncReviewFields } from "./CamundaSyncReviewFields";
import { DatabricksSyncReviewFields } from "./DatabricksSyncReviewFields"; import { DatabricksSyncReviewFields } from "./DatabricksSyncReviewFields";
import { GcpSyncReviewFields } from "./GcpSyncReviewFields"; import { GcpSyncReviewFields } from "./GcpSyncReviewFields";
import { GitHubSyncReviewFields } from "./GitHubSyncReviewFields"; import { GitHubSyncReviewFields } from "./GitHubSyncReviewFields";
@@ -77,6 +78,9 @@ export const SecretSyncReviewFields = () => {
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
DestinationFieldsComponent = <TerraformCloudSyncReviewFields />; DestinationFieldsComponent = <TerraformCloudSyncReviewFields />;
break; break;
case SecretSync.Camunda:
DestinationFieldsComponent = <CamundaSyncReviewFields />;
break;
case SecretSync.Vercel: case SecretSync.Vercel:
DestinationFieldsComponent = <VercelSyncReviewFields />; DestinationFieldsComponent = <VercelSyncReviewFields />;
break; break;
@@ -0,0 +1,15 @@
import { z } from "zod";
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const CamundaSyncDestinationSchema = BaseSecretSyncSchema().merge(
z.object({
destination: z.literal(SecretSync.Camunda),
destinationConfig: z.object({
scope: z.string().trim().min(1, "Camunda scope required"),
clusterUUID: z.string().trim().min(1, "Camunda cluster UUID required"),
clusterName: z.string().optional()
})
})
);
@@ -7,6 +7,7 @@ import { GitHubSyncDestinationSchema } from "@app/components/secret-syncs/forms/
import { AwsParameterStoreSyncDestinationSchema } from "./aws-parameter-store-sync-destination-schema"; import { AwsParameterStoreSyncDestinationSchema } from "./aws-parameter-store-sync-destination-schema";
import { AzureAppConfigurationSyncDestinationSchema } from "./azure-app-configuration-sync-destination-schema"; import { AzureAppConfigurationSyncDestinationSchema } from "./azure-app-configuration-sync-destination-schema";
import { AzureKeyVaultSyncDestinationSchema } from "./azure-key-vault-sync-destination-schema"; import { AzureKeyVaultSyncDestinationSchema } from "./azure-key-vault-sync-destination-schema";
import { CamundaSyncDestinationSchema } from "./camunda-sync-destination-schema";
import { GcpSyncDestinationSchema } from "./gcp-sync-destination-schema"; import { GcpSyncDestinationSchema } from "./gcp-sync-destination-schema";
import { HumanitecSyncDestinationSchema } from "./humanitec-sync-destination-schema"; import { HumanitecSyncDestinationSchema } from "./humanitec-sync-destination-schema";
import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destination-schema"; import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destination-schema";
@@ -22,6 +23,7 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
DatabricksSyncDestinationSchema, DatabricksSyncDestinationSchema,
HumanitecSyncDestinationSchema, HumanitecSyncDestinationSchema,
TerraformCloudSyncDestinationSchema, TerraformCloudSyncDestinationSchema,
CamundaSyncDestinationSchema,
VercelSyncDestinationSchema VercelSyncDestinationSchema
]); ]);
+5 -1
View File
@@ -6,6 +6,7 @@ import {
AwsConnectionMethod, AwsConnectionMethod,
AzureAppConfigurationConnectionMethod, AzureAppConfigurationConnectionMethod,
AzureKeyVaultConnectionMethod, AzureKeyVaultConnectionMethod,
CamundaConnectionMethod,
DatabricksConnectionMethod, DatabricksConnectionMethod,
GcpConnectionMethod, GcpConnectionMethod,
GitHubConnectionMethod, GitHubConnectionMethod,
@@ -34,7 +35,8 @@ export const APP_CONNECTION_MAP: Record<AppConnection, { name: string; image: st
[AppConnection.TerraformCloud]: { name: "Terraform Cloud", image: "Terraform Cloud.png" }, [AppConnection.TerraformCloud]: { name: "Terraform Cloud", image: "Terraform Cloud.png" },
[AppConnection.Vercel]: { name: "Vercel", image: "Vercel.png" }, [AppConnection.Vercel]: { name: "Vercel", image: "Vercel.png" },
[AppConnection.Postgres]: { name: "PostgreSQL", image: "Postgres.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"]) => { export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
@@ -53,6 +55,8 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
return { name: "Service Account Impersonation", icon: faUser }; return { name: "Service Account Impersonation", icon: faUser };
case DatabricksConnectionMethod.ServicePrincipal: case DatabricksConnectionMethod.ServicePrincipal:
return { name: "Service Principal", icon: faUser }; return { name: "Service Principal", icon: faUser };
case CamundaConnectionMethod.ClientCredentials:
return { name: "Client Credentials", icon: faKey };
case HumanitecConnectionMethod.ApiToken: case HumanitecConnectionMethod.ApiToken:
case TerraformCloudConnectionMethod.ApiToken: case TerraformCloudConnectionMethod.ApiToken:
case VercelConnectionMethod.ApiToken: case VercelConnectionMethod.ApiToken:
+5
View File
@@ -28,6 +28,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
name: "Terraform Cloud", name: "Terraform Cloud",
image: "Terraform Cloud.png" image: "Terraform Cloud.png"
}, },
[SecretSync.Camunda]: {
name: "Camunda",
image: "Camunda.png"
},
[SecretSync.Vercel]: { [SecretSync.Vercel]: {
name: "Vercel", name: "Vercel",
image: "Vercel.png" image: "Vercel.png"
@@ -44,6 +48,7 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Databricks]: AppConnection.Databricks, [SecretSync.Databricks]: AppConnection.Databricks,
[SecretSync.Humanitec]: AppConnection.Humanitec, [SecretSync.Humanitec]: AppConnection.Humanitec,
[SecretSync.TerraformCloud]: AppConnection.TerraformCloud, [SecretSync.TerraformCloud]: AppConnection.TerraformCloud,
[SecretSync.Camunda]: AppConnection.Camunda,
[SecretSync.Vercel]: AppConnection.Vercel [SecretSync.Vercel]: AppConnection.Vercel
}; };
@@ -0,0 +1 @@
export * from "./queries";
@@ -0,0 +1,37 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "../queries";
import { TCamundaCluster } from "./types";
const camundaConnectionKeys = {
all: [...appConnectionKeys.all, "camunda"] as const,
listClusters: (connectionId: string) =>
[...camundaConnectionKeys.all, "clusters", connectionId] as const
};
export const useCamundaConnectionListClusters = (
connectionId: string,
options?: Omit<
UseQueryOptions<
TCamundaCluster[],
unknown,
TCamundaCluster[],
ReturnType<typeof camundaConnectionKeys.listClusters>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: camundaConnectionKeys.listClusters(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<{ clusters: TCamundaCluster[] }>(
`/api/v1/app-connections/camunda/${connectionId}/clusters`
);
return data.clusters;
},
...options
});
};
@@ -0,0 +1,4 @@
export type TCamundaCluster = {
name: string;
uuid: string;
};
@@ -9,5 +9,6 @@ export enum AppConnection {
TerraformCloud = "terraform-cloud", TerraformCloud = "terraform-cloud",
Vercel = "vercel", Vercel = "vercel",
Postgres = "postgres", Postgres = "postgres",
MsSql = "mssql" MsSql = "mssql",
Camunda = "camunda"
} }
@@ -55,6 +55,10 @@ export type TMsSqlConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.MsSql; app: AppConnection.MsSql;
}; };
export type TCamundaConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Camunda;
};
export type TAppConnectionOption = export type TAppConnectionOption =
| TAwsConnectionOption | TAwsConnectionOption
| TGitHubConnectionOption | TGitHubConnectionOption
@@ -66,7 +70,8 @@ export type TAppConnectionOption =
| TTerraformCloudConnectionOption | TTerraformCloudConnectionOption
| TVercelConnectionOption | TVercelConnectionOption
| TPostgresConnectionOption | TPostgresConnectionOption
| TMsSqlConnectionOption; | TMsSqlConnectionOption
| TCamundaConnectionOption;
export type TAppConnectionOptionMap = { export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption; [AppConnection.AWS]: TAwsConnectionOption;
@@ -80,4 +85,5 @@ export type TAppConnectionOptionMap = {
[AppConnection.Vercel]: TVercelConnectionOption; [AppConnection.Vercel]: TVercelConnectionOption;
[AppConnection.Postgres]: TPostgresConnectionOption; [AppConnection.Postgres]: TPostgresConnectionOption;
[AppConnection.MsSql]: TMsSqlConnectionOption; [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 { TAwsConnection } from "./aws-connection";
import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection"; import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection";
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection"; import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
import { TCamundaConnection } from "./camunda-connection";
import { TDatabricksConnection } from "./databricks-connection"; import { TDatabricksConnection } from "./databricks-connection";
import { TGcpConnection } from "./gcp-connection"; import { TGcpConnection } from "./gcp-connection";
import { TGitHubConnection } from "./github-connection"; import { TGitHubConnection } from "./github-connection";
@@ -15,6 +16,7 @@ import { TVercelConnection } from "./vercel-connection";
export * from "./aws-connection"; export * from "./aws-connection";
export * from "./azure-app-configuration-connection"; export * from "./azure-app-configuration-connection";
export * from "./azure-key-vault-connection"; export * from "./azure-key-vault-connection";
export * from "./camunda-connection";
export * from "./databricks-connection"; export * from "./databricks-connection";
export * from "./gcp-connection"; export * from "./gcp-connection";
export * from "./github-connection"; export * from "./github-connection";
@@ -35,7 +37,8 @@ export type TAppConnection =
| TTerraformCloudConnection | TTerraformCloudConnection
| TVercelConnection | TVercelConnection
| TPostgresConnection | TPostgresConnection
| TMsSqlConnection; | TMsSqlConnection
| TCamundaConnection;
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">; export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
@@ -74,4 +77,5 @@ export type TAppConnectionMap = {
[AppConnection.Vercel]: TVercelConnection; [AppConnection.Vercel]: TVercelConnection;
[AppConnection.Postgres]: TPostgresConnection; [AppConnection.Postgres]: TPostgresConnection;
[AppConnection.MsSql]: TMsSqlConnection; [AppConnection.MsSql]: TMsSqlConnection;
[AppConnection.Camunda]: TCamundaConnection;
}; };
@@ -8,6 +8,7 @@ export enum SecretSync {
Databricks = "databricks", Databricks = "databricks",
Humanitec = "humanitec", Humanitec = "humanitec",
TerraformCloud = "terraform-cloud", TerraformCloud = "terraform-cloud",
Camunda = "camunda",
Vercel = "vercel" Vercel = "vercel"
} }
@@ -0,0 +1,21 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
export enum CamundaSyncScope {
Cluster = "cluster"
}
export type TCamundaSync = TRootSecretSync & {
destination: SecretSync.Camunda;
destinationConfig: {
scope: string;
clusterUUID: string;
clusterName?: string;
};
connection: {
app: AppConnection.Camunda;
name: string;
id: string;
};
};
@@ -7,6 +7,7 @@ import { DiscriminativePick } from "@app/types";
import { TAwsSecretsManagerSync } from "./aws-secrets-manager-sync"; import { TAwsSecretsManagerSync } from "./aws-secrets-manager-sync";
import { TAzureAppConfigurationSync } from "./azure-app-configuration-sync"; import { TAzureAppConfigurationSync } from "./azure-app-configuration-sync";
import { TAzureKeyVaultSync } from "./azure-key-vault-sync"; import { TAzureKeyVaultSync } from "./azure-key-vault-sync";
import { TCamundaSync } from "./camunda-sync";
import { TGcpSync } from "./gcp-sync"; import { TGcpSync } from "./gcp-sync";
import { THumanitecSync } from "./humanitec-sync"; import { THumanitecSync } from "./humanitec-sync";
import { TTerraformCloudSync } from "./terraform-cloud-sync"; import { TTerraformCloudSync } from "./terraform-cloud-sync";
@@ -28,6 +29,7 @@ export type TSecretSync =
| TDatabricksSync | TDatabricksSync
| THumanitecSync | THumanitecSync
| TTerraformCloudSync | TTerraformCloudSync
| TCamundaSync
| TVercelSync; | TVercelSync;
export type TListSecretSyncs = { secretSyncs: TSecretSync[] }; export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
@@ -12,6 +12,7 @@ import { AppConnectionHeader } from "../AppConnectionHeader";
import { AwsConnectionForm } from "./AwsConnectionForm"; import { AwsConnectionForm } from "./AwsConnectionForm";
import { AzureAppConfigurationConnectionForm } from "./AzureAppConfigurationConnectionForm"; import { AzureAppConfigurationConnectionForm } from "./AzureAppConfigurationConnectionForm";
import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm"; import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm";
import { CamundaConnectionForm } from "./CamundaConnectionForm";
import { DatabricksConnectionForm } from "./DatabricksConnectionForm"; import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
import { GcpConnectionForm } from "./GcpConnectionForm"; import { GcpConnectionForm } from "./GcpConnectionForm";
import { GitHubConnectionForm } from "./GitHubConnectionForm"; import { GitHubConnectionForm } from "./GitHubConnectionForm";
@@ -80,6 +81,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
return <PostgresConnectionForm onSubmit={onSubmit} />; return <PostgresConnectionForm onSubmit={onSubmit} />;
case AppConnection.MsSql: case AppConnection.MsSql:
return <MsSqlConnectionForm onSubmit={onSubmit} />; return <MsSqlConnectionForm onSubmit={onSubmit} />;
case AppConnection.Camunda:
return <CamundaConnectionForm onSubmit={onSubmit} />;
default: default:
throw new Error(`Unhandled App ${app}`); throw new Error(`Unhandled App ${app}`);
} }
@@ -138,6 +141,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return <PostgresConnectionForm onSubmit={onSubmit} appConnection={appConnection} />; return <PostgresConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.MsSql: case AppConnection.MsSql:
return <MsSqlConnectionForm onSubmit={onSubmit} appConnection={appConnection} />; return <MsSqlConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.Camunda:
return <CamundaConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
default: default:
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`); 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="YrBDnQYLevSe40PJ" />
</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>
);
};
@@ -0,0 +1,29 @@
import { useCamundaConnectionListClusters } from "@app/hooks/api/appConnections/camunda";
import { TCamundaSync } from "@app/hooks/api/secretSyncs/types/camunda-sync";
import { getSecretSyncDestinationColValues } from "../helpers";
import { SecretSyncTableCell } from "../SecretSyncTableCell";
type Props = {
secretSync: TCamundaSync;
};
export const CamundaSyncDestinationCol = ({ secretSync }: Props) => {
const { data: clusters, isPending } = useCamundaConnectionListClusters(secretSync.connectionId);
const { primaryText, secondaryText } = getSecretSyncDestinationColValues({
...secretSync,
destinationConfig: {
...secretSync.destinationConfig,
clusterName: clusters?.find(
(cluster) => cluster.uuid === secretSync.destinationConfig.clusterUUID
)?.name
}
});
if (isPending) {
return <SecretSyncTableCell primaryText="Loading cluster info..." secondaryText="Cluster" />;
}
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
};
@@ -4,6 +4,7 @@ import { AwsParameterStoreSyncDestinationCol } from "./AwsParameterStoreSyncDest
import { AwsSecretsManagerSyncDestinationCol } from "./AwsSecretsManagerSyncDestinationCol"; import { AwsSecretsManagerSyncDestinationCol } from "./AwsSecretsManagerSyncDestinationCol";
import { AzureAppConfigurationDestinationSyncCol } from "./AzureAppConfigurationDestinationSyncCol"; import { AzureAppConfigurationDestinationSyncCol } from "./AzureAppConfigurationDestinationSyncCol";
import { AzureKeyVaultDestinationSyncCol } from "./AzureKeyVaultDestinationSyncCol"; import { AzureKeyVaultDestinationSyncCol } from "./AzureKeyVaultDestinationSyncCol";
import { CamundaSyncDestinationCol } from "./CamundaSyncDestinationCol";
import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol"; import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol";
import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol"; import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol";
import { GitHubSyncDestinationCol } from "./GitHubSyncDestinationCol"; import { GitHubSyncDestinationCol } from "./GitHubSyncDestinationCol";
@@ -35,6 +36,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
return <HumanitecSyncDestinationCol secretSync={secretSync} />; return <HumanitecSyncDestinationCol secretSync={secretSync} />;
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
return <TerraformCloudSyncDestinationCol secretSync={secretSync} />; return <TerraformCloudSyncDestinationCol secretSync={secretSync} />;
case SecretSync.Camunda:
return <CamundaSyncDestinationCol secretSync={secretSync} />;
case SecretSync.Vercel: case SecretSync.Vercel:
return <VercelSyncDestinationCol secretSync={secretSync} />; return <VercelSyncDestinationCol secretSync={secretSync} />;
default: default:
@@ -82,6 +82,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
secondaryText = destinationConfig.workspaceName; secondaryText = destinationConfig.workspaceName;
} }
break; break;
case SecretSync.Camunda:
primaryText = destinationConfig.clusterName ?? destinationConfig.clusterUUID;
secondaryText = "Cluster";
break;
case SecretSync.Vercel: case SecretSync.Vercel:
primaryText = destinationConfig.appName || destinationConfig.app; primaryText = destinationConfig.appName || destinationConfig.app;
secondaryText = destinationConfig.env; secondaryText = destinationConfig.env;
@@ -0,0 +1,21 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { useCamundaConnectionListClusters } from "@app/hooks/api/appConnections/camunda";
import { TCamundaSync } from "@app/hooks/api/secretSyncs/types/camunda-sync";
type Props = {
secretSync: TCamundaSync;
};
export const CamundaSyncDestinationSection = ({ secretSync }: Props) => {
const { data: clusters, isPending } = useCamundaConnectionListClusters(secretSync.connectionId);
const {
destinationConfig: { clusterUUID }
} = secretSync;
if (isPending) {
return <GenericFieldLabel label="Cluster">Loading...</GenericFieldLabel>;
}
const clusterName = clusters?.find((cluster) => cluster.uuid === clusterUUID)?.name;
return <GenericFieldLabel label="Cluster">{clusterName ?? clusterUUID}</GenericFieldLabel>;
};
@@ -16,6 +16,7 @@ import { GitHubSyncDestinationSection } from "@app/pages/secret-manager/SecretSy
import { AzureAppConfigurationSyncDestinationSection } from "./AzureAppConfigurationSyncDestinationSection"; import { AzureAppConfigurationSyncDestinationSection } from "./AzureAppConfigurationSyncDestinationSection";
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection"; import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
import { CamundaSyncDestinationSection } from "./CamundaSyncDestinationSection";
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection"; import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection"; import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection"; import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection";
@@ -62,6 +63,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
DestinationComponents = <TerraformCloudSyncDestinationSection secretSync={secretSync} />; DestinationComponents = <TerraformCloudSyncDestinationSection secretSync={secretSync} />;
break; break;
case SecretSync.Camunda:
DestinationComponents = <CamundaSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.Vercel: case SecretSync.Vercel:
DestinationComponents = <VercelSyncDestinationSection secretSync={secretSync} />; DestinationComponents = <VercelSyncDestinationSection secretSync={secretSync} />;
break; break;
@@ -1,6 +1,7 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { GenericFieldLabel } from "@app/components/secret-syncs"; import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TerraformCloudSyncCategory } from "@app/hooks/api/appConnections/terraform-cloud";
import { import {
TerraformCloudSyncScope, TerraformCloudSyncScope,
TTerraformCloudSync TTerraformCloudSync
@@ -22,6 +23,13 @@ export const TerraformCloudSyncDestinationSection = ({ secretSync }: Props) => {
<GenericFieldLabel label="Variable Set"> <GenericFieldLabel label="Variable Set">
{destinationConfig.variableSetName} {destinationConfig.variableSetName}
</GenericFieldLabel> </GenericFieldLabel>
<GenericFieldLabel label="Category">
{Object.keys(TerraformCloudSyncCategory).find(
(key) =>
TerraformCloudSyncCategory[key as keyof typeof TerraformCloudSyncCategory] ===
destinationConfig.category
)}
</GenericFieldLabel>
</> </>
); );
break; break;
@@ -30,6 +38,13 @@ export const TerraformCloudSyncDestinationSection = ({ secretSync }: Props) => {
<> <>
<GenericFieldLabel label="Organization">{destinationConfig.org}</GenericFieldLabel> <GenericFieldLabel label="Organization">{destinationConfig.org}</GenericFieldLabel>
<GenericFieldLabel label="Workspace">{destinationConfig.workspaceName}</GenericFieldLabel> <GenericFieldLabel label="Workspace">{destinationConfig.workspaceName}</GenericFieldLabel>
<GenericFieldLabel label="Category">
{Object.keys(TerraformCloudSyncCategory).find(
(key) =>
TerraformCloudSyncCategory[key as keyof typeof TerraformCloudSyncCategory] ===
destinationConfig.category
)}
</GenericFieldLabel>
</> </>
); );
break; break;
@@ -49,6 +49,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
case SecretSync.Databricks: case SecretSync.Databricks:
case SecretSync.Humanitec: case SecretSync.Humanitec:
case SecretSync.TerraformCloud: case SecretSync.TerraformCloud:
case SecretSync.Camunda:
case SecretSync.Vercel: case SecretSync.Vercel:
AdditionalSyncOptionsComponent = null; AdditionalSyncOptionsComponent = null;
break; break;