Merge pull request #1145 from atimapreandrew/checkly-sync-on-group-level

Checkly sync on group level
This commit is contained in:
BlackMagiq
2023-11-02 11:10:40 +02:00
committed by GitHub
16 changed files with 358 additions and 124 deletions
@@ -10,6 +10,7 @@ import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
INTEGRATION_BITBUCKET_API_URL,
INTEGRATION_CHECKLY_API_URL,
INTEGRATION_GCP_SECRET_MANAGER,
INTEGRATION_NORTHFLANK_API_URL,
INTEGRATION_QOVERY_API_URL,
@@ -344,6 +345,59 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon
});
};
/**
* Return list of Checkly groups for a specific user
* @param req
* @param res
*/
export const getIntegrationAuthChecklyGroups = async (req: Request, res: Response) => {
const {
params: { integrationAuthId },
query: { accountId }
} = await validateRequest(reqValidator.GetIntegrationAuthChecklyGroupsV1, req);
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
integrationAuthId: new Types.ObjectId(integrationAuthId)
});
const { permission } = await getUserProjectPermissions(
req.user._id,
integrationAuth.workspace.toString()
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.Integrations
);
interface ChecklyGroup {
id: number;
name: string;
}
if (accountId && accountId !== "") {
const { data }: { data: ChecklyGroup[] } = (
await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"X-Checkly-Account": accountId
}
})
);
return res.status(200).send({
groups: data.map((g: ChecklyGroup) => ({
name: g.name,
groupId: g.id,
}))
});
}
return res.status(200).send({
groups: []
});
}
/**
* Return list of Qovery Orgs for a specific user
* @param req
+1 -1
View File
@@ -911,7 +911,7 @@ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
};
/**
* Return list of projects for the Checkly integration
* Return list of accounts for the Checkly integration
* @param {Object} obj
* @param {String} obj.accessToken - api key for the Checkly API
* @returns {Object[]} apps - Сheckly accounts
+141 -81
View File
@@ -2104,7 +2104,7 @@ const syncSecretsSupabase = async ({
};
/**
* Sync/push [secrets] to Checkly app
* Sync/push [secrets] to Checkly app/group
* @param {Object} obj
* @param {IIntegration} obj.integration - integration details
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
@@ -2121,94 +2121,154 @@ const syncSecretsCheckly = async ({
accessToken: string;
appendices?: { prefix: string; suffix: string };
}) => {
let getSecretsRes = (
await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/variables`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
"X-Checkly-Account": integration.appId
}
})
).data.reduce(
(obj: any, secret: any) => ({
...obj,
[secret.key]: secret.value
}),
{}
);
getSecretsRes = Object.keys(getSecretsRes).reduce(
(
result: {
[key: string]: string;
},
key
) => {
if (
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) &&
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true)
) {
result[key] = getSecretsRes[key];
}
return result;
},
{}
);
if (integration.targetServiceId) {
// sync secrets to checkly group envars
// add secrets
for await (const key of Object.keys(secrets)) {
if (!(key in getSecretsRes)) {
// case: secret does not exist in checkly
// -> add secret
await standardRequest.post(
`${INTEGRATION_CHECKLY_API_URL}/v1/variables`,
{
key,
value: secrets[key].value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Content-Type": "application/json",
"X-Checkly-Account": integration.appId
}
}
);
} else {
// case: secret exists in checkly
// -> update/set secret
if (secrets[key] !== getSecretsRes[key]) {
await standardRequest.put(
`${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`,
{
value: secrets[key].value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
Accept: "application/json",
"X-Checkly-Account": integration.appId
}
}
);
}
}
}
for await (const key of Object.keys(getSecretsRes)) {
if (!(key in secrets)) {
// delete secret
await standardRequest.delete(`${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`, {
let getGroupSecretsRes = (
await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"X-Checkly-Account": integration.appId
}
});
})
).data.environmentVariables.reduce(
(obj: any, secret: any) => ({
...obj,
[secret.key]: secret.value
}),
{}
);
getGroupSecretsRes = Object.keys(getGroupSecretsRes).reduce(
(
result: {
[key: string]: string;
},
key
) => {
if (
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) &&
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true)
) {
result[key] = getGroupSecretsRes[key];
}
return result;
},
{}
);
const groupEnvironmentVariables = Object.keys(secrets).map(key => ({
key,
value: secrets[key].value
}));
await standardRequest.put(
`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`,
{
environmentVariables: groupEnvironmentVariables
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"X-Checkly-Account": integration.appId
}
}
);
} else {
// sync secrets to checkly global envars
let getSecretsRes = (
await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/variables`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
"X-Checkly-Account": integration.appId
}
})
).data.reduce(
(obj: any, secret: any) => ({
...obj,
[secret.key]: secret.value
}),
{}
);
getSecretsRes = Object.keys(getSecretsRes).reduce(
(
result: {
[key: string]: string;
},
key
) => {
if (
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) &&
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true)
) {
result[key] = getSecretsRes[key];
}
return result;
},
{}
);
// add secrets
for await (const key of Object.keys(secrets)) {
if (!(key in getSecretsRes)) {
// case: secret does not exist in checkly
// -> add secret
await standardRequest.post(
`${INTEGRATION_CHECKLY_API_URL}/v1/variables`,
{
key,
value: secrets[key].value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Content-Type": "application/json",
"X-Checkly-Account": integration.appId
}
}
);
} else {
// case: secret exists in checkly
// -> update/set secret
if (secrets[key] !== getSecretsRes[key]) {
await standardRequest.put(
`${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`,
{
value: secrets[key].value
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
Accept: "application/json",
"X-Checkly-Account": integration.appId
}
}
);
}
}
}
for await (const key of Object.keys(getSecretsRes)) {
if (!(key in secrets)) {
// delete secret
await standardRequest.delete(`${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"X-Checkly-Account": integration.appId
}
});
}
}
}
};
+8
View File
@@ -60,6 +60,14 @@ router.get(
integrationAuthController.getIntegrationAuthVercelBranches
);
router.get(
"/:integrationAuthId/checkly/groups",
requireAuth({
acceptedAuthModes: [AuthMode.JWT]
}),
integrationAuthController.getIntegrationAuthChecklyGroups
);
router.get(
"/:integrationAuthId/qovery/orgs",
requireAuth({
+2 -1
View File
@@ -1,11 +1,12 @@
import Redis, { Redis as TRedis } from "ioredis";
import { logger } from "../utils/logging";
let redisClient: TRedis | null;
if (process.env.REDIS_URL) {
redisClient = new Redis(process.env.REDIS_URL as string);
} else {
console.warn("Redis URL not set, skipping Redis initialization.");
logger.warn("Redis URL not set, skipping Redis initialization.");
redisClient = null;
}
+2 -2
View File
@@ -11,7 +11,6 @@ import {
backfillBots,
backfillEncryptionMetadata,
backfillIntegration,
backfillPermission,
backfillSecretBlindIndexData,
backfillSecretFolders,
backfillSecretVersions,
@@ -28,6 +27,7 @@ import {
} from "./reencryptData";
import { getMongoURL, getNodeEnv, getRedisUrl, getSentryDSN } from "../../config";
import { initializePassport } from "../auth";
import { logger } from "../logging";
/**
* Prepare Infisical upon startup. This includes tasks like:
@@ -41,7 +41,7 @@ import { initializePassport } from "../auth";
*/
export const setup = async () => {
if ((await getRedisUrl()) === undefined || (await getRedisUrl()) === "") {
console.error(
logger.error(
"WARNING: Redis is not yet configured. Infisical may not function as expected without it."
);
}
@@ -117,6 +117,15 @@ export const GetIntegrationAuthVercelBranchesV1 = z.object({
})
});
export const GetIntegrationAuthChecklyGroupsV1 = z.object({
params: z.object({
integrationAuthId: z.string().trim()
}),
query: z.object({
accountId: z.string().trim()
})
});
export const GetIntegrationAuthQoveryOrgsV1 = z.object({
params: z.object({
integrationAuthId: z.string().trim()