mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
feat: Checkly app connection and secrets sync (#4078)
* feat: checkly app connection --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
greptile-apps[bot]
parent
023079be16
commit
7ef077228e
@@ -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 CHECKLY_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Checkly",
|
||||
destination: SecretSync.Checkly,
|
||||
connection: AppConnection.Checkly,
|
||||
canImportSecrets: false
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
/* eslint-disable no-continue */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { ChecklyPublicAPI } from "@app/services/app-connection/checkly/checkly-connection-public-client";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
import { TChecklySyncWithCredentials } from "./checkly-sync-types";
|
||||
|
||||
export const ChecklySyncFns = {
|
||||
async getSecrets(secretSync: TChecklySyncWithCredentials) {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
},
|
||||
|
||||
async syncSecrets(secretSync: TChecklySyncWithCredentials, secretMap: TSecretMap) {
|
||||
const {
|
||||
environment,
|
||||
syncOptions: { disableSecretDeletion, keySchema }
|
||||
} = secretSync;
|
||||
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
const variables = await ChecklyPublicAPI.getVariables(secretSync.connection, config.accountId);
|
||||
|
||||
const checklySecrets = Object.fromEntries(variables!.map((variable) => [variable.key, variable]));
|
||||
|
||||
for await (const key of Object.keys(secretMap)) {
|
||||
try {
|
||||
const entry = secretMap[key];
|
||||
|
||||
// If value is empty, we skip the upsert - checkly does not allow empty values
|
||||
if (entry.value.trim() === "") {
|
||||
// Delete the secret from Checkly if its empty
|
||||
if (!disableSecretDeletion) {
|
||||
await ChecklyPublicAPI.deleteVariable(secretSync.connection, config.accountId, {
|
||||
key
|
||||
});
|
||||
}
|
||||
continue; // Skip empty values
|
||||
}
|
||||
|
||||
await ChecklyPublicAPI.upsertVariable(secretSync.connection, config.accountId, {
|
||||
key,
|
||||
value: entry.value,
|
||||
secret: true,
|
||||
locked: true
|
||||
});
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (disableSecretDeletion) return;
|
||||
|
||||
for await (const key of Object.keys(checklySecrets)) {
|
||||
try {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, environment?.slug || "", keySchema)) continue;
|
||||
|
||||
if (!secretMap[key]) {
|
||||
await ChecklyPublicAPI.deleteVariable(secretSync.connection, config.accountId, {
|
||||
key
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async removeSecrets(secretSync: TChecklySyncWithCredentials, secretMap: TSecretMap) {
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
const variables = await ChecklyPublicAPI.getVariables(secretSync.connection, config.accountId);
|
||||
|
||||
const checklySecrets = Object.fromEntries(variables!.map((variable) => [variable.key, variable]));
|
||||
|
||||
for await (const secret of Object.keys(checklySecrets)) {
|
||||
try {
|
||||
if (secret in secretMap) {
|
||||
await ChecklyPublicAPI.deleteVariable(secretSync.connection, config.accountId, {
|
||||
key: secret
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: secret
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { z } from "zod";
|
||||
|
||||
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 ChecklySyncDestinationConfigSchema = z.object({
|
||||
accountId: z.string().min(1, "Account ID is required").max(255, "Account ID must be less than 255 characters"),
|
||||
accountName: z.string().min(1, "Account Name is required").max(255, "Account ID must be less than 255 characters")
|
||||
});
|
||||
|
||||
const ChecklySyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const ChecklySyncSchema = BaseSecretSyncSchema(SecretSync.Checkly, ChecklySyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.Checkly),
|
||||
destinationConfig: ChecklySyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateChecklySyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Checkly,
|
||||
ChecklySyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: ChecklySyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateChecklySyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Checkly,
|
||||
ChecklySyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: ChecklySyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const ChecklySyncListItemSchema = z.object({
|
||||
name: z.literal("Checkly"),
|
||||
connection: z.literal(AppConnection.Checkly),
|
||||
destination: z.literal(SecretSync.Checkly),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TChecklyConnection, TChecklyVariable } from "@app/services/app-connection/checkly";
|
||||
|
||||
import { ChecklySyncListItemSchema, ChecklySyncSchema, CreateChecklySyncSchema } from "./checkly-sync-schemas";
|
||||
|
||||
export type TChecklySyncListItem = z.infer<typeof ChecklySyncListItemSchema>;
|
||||
|
||||
export type TChecklySync = z.infer<typeof ChecklySyncSchema>;
|
||||
|
||||
export type TChecklySyncInput = z.infer<typeof CreateChecklySyncSchema>;
|
||||
|
||||
export type TChecklySyncWithCredentials = TChecklySync & {
|
||||
connection: TChecklyConnection;
|
||||
};
|
||||
|
||||
export type TChecklySecret = TChecklyVariable;
|
||||
|
||||
export type TChecklyVariablesGraphResponse = {
|
||||
data: {
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
};
|
||||
@@ -24,7 +24,8 @@ export enum SecretSync {
|
||||
CloudflareWorkers = "cloudflare-workers",
|
||||
|
||||
Zabbix = "zabbix",
|
||||
Railway = "railway"
|
||||
Railway = "railway",
|
||||
Checkly = "checkly"
|
||||
}
|
||||
|
||||
export enum SecretSyncInitialSyncBehavior {
|
||||
|
||||
@@ -29,6 +29,8 @@ import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFact
|
||||
import { AZURE_DEVOPS_SYNC_LIST_OPTION, azureDevOpsSyncFactory } from "./azure-devops";
|
||||
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
|
||||
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
||||
import { CHECKLY_SYNC_LIST_OPTION } from "./checkly/checkly-sync-constants";
|
||||
import { ChecklySyncFns } from "./checkly/checkly-sync-fns";
|
||||
import { CLOUDFLARE_PAGES_SYNC_LIST_OPTION } from "./cloudflare-pages/cloudflare-pages-constants";
|
||||
import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns";
|
||||
import { CLOUDFLARE_WORKERS_SYNC_LIST_OPTION, CloudflareWorkersSyncFns } from "./cloudflare-workers";
|
||||
@@ -76,7 +78,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.CloudflareWorkers]: CLOUDFLARE_WORKERS_SYNC_LIST_OPTION,
|
||||
|
||||
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION,
|
||||
[SecretSync.Railway]: RAILWAY_SYNC_LIST_OPTION
|
||||
[SecretSync.Railway]: RAILWAY_SYNC_LIST_OPTION,
|
||||
[SecretSync.Checkly]: CHECKLY_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretSyncOptions = () => {
|
||||
@@ -250,6 +253,8 @@ export const SecretSyncFns = {
|
||||
return ZabbixSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
return RailwaySyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Checkly:
|
||||
return ChecklySyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -351,6 +356,9 @@ export const SecretSyncFns = {
|
||||
case SecretSync.Railway:
|
||||
secretMap = await RailwaySyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.Checkly:
|
||||
secretMap = await ChecklySyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -434,6 +442,8 @@ export const SecretSyncFns = {
|
||||
return ZabbixSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
return RailwaySyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Checkly:
|
||||
return ChecklySyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
|
||||
@@ -27,7 +27,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.CloudflareWorkers]: "Cloudflare Workers",
|
||||
|
||||
[SecretSync.Zabbix]: "Zabbix",
|
||||
[SecretSync.Railway]: "Railway"
|
||||
[SecretSync.Railway]: "Railway",
|
||||
[SecretSync.Checkly]: "Checkly"
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
@@ -56,7 +57,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.CloudflareWorkers]: AppConnection.Cloudflare,
|
||||
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix,
|
||||
[SecretSync.Railway]: AppConnection.Railway
|
||||
[SecretSync.Railway]: AppConnection.Railway,
|
||||
[SecretSync.Checkly]: AppConnection.Checkly
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
@@ -85,5 +87,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.CloudflareWorkers]: SecretSyncPlanType.Regular,
|
||||
|
||||
[SecretSync.Zabbix]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Railway]: SecretSyncPlanType.Regular
|
||||
[SecretSync.Railway]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Checkly]: SecretSyncPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -72,6 +72,12 @@ import {
|
||||
TAzureKeyVaultSyncListItem,
|
||||
TAzureKeyVaultSyncWithCredentials
|
||||
} from "./azure-key-vault";
|
||||
import {
|
||||
TChecklySync,
|
||||
TChecklySyncInput,
|
||||
TChecklySyncListItem,
|
||||
TChecklySyncWithCredentials
|
||||
} from "./checkly/checkly-sync-types";
|
||||
import {
|
||||
TCloudflarePagesSync,
|
||||
TCloudflarePagesSyncInput,
|
||||
@@ -152,7 +158,8 @@ export type TSecretSync =
|
||||
| TCloudflarePagesSync
|
||||
| TCloudflareWorkersSync
|
||||
| TZabbixSync
|
||||
| TRailwaySync;
|
||||
| TRailwaySync
|
||||
| TChecklySync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
| TAwsParameterStoreSyncWithCredentials
|
||||
@@ -179,7 +186,8 @@ export type TSecretSyncWithCredentials =
|
||||
| TCloudflarePagesSyncWithCredentials
|
||||
| TCloudflareWorkersSyncWithCredentials
|
||||
| TZabbixSyncWithCredentials
|
||||
| TRailwaySyncWithCredentials;
|
||||
| TRailwaySyncWithCredentials
|
||||
| TChecklySyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
| TAwsParameterStoreSyncInput
|
||||
@@ -206,7 +214,8 @@ export type TSecretSyncInput =
|
||||
| TCloudflarePagesSyncInput
|
||||
| TCloudflareWorkersSyncInput
|
||||
| TZabbixSyncInput
|
||||
| TRailwaySyncInput;
|
||||
| TRailwaySyncInput
|
||||
| TChecklySyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
| TAwsParameterStoreSyncListItem
|
||||
@@ -233,7 +242,8 @@ export type TSecretSyncListItem =
|
||||
| TCloudflarePagesSyncListItem
|
||||
| TCloudflareWorkersSyncListItem
|
||||
| TZabbixSyncListItem
|
||||
| TRailwaySyncListItem;
|
||||
| TRailwaySyncListItem
|
||||
| TChecklySyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
canImportSecrets: boolean;
|
||||
|
||||
Reference in New Issue
Block a user