mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 06:28:11 +00:00
Merge pull request #1145 from atimapreandrew/checkly-sync-on-group-level
Checkly sync on group level
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
INTEGRATION_BITBUCKET_API_URL,
|
INTEGRATION_BITBUCKET_API_URL,
|
||||||
|
INTEGRATION_CHECKLY_API_URL,
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_NORTHFLANK_API_URL,
|
INTEGRATION_NORTHFLANK_API_URL,
|
||||||
INTEGRATION_QOVERY_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
|
* Return list of Qovery Orgs for a specific user
|
||||||
* @param req
|
* @param req
|
||||||
|
|||||||
@@ -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 {Object} obj
|
||||||
* @param {String} obj.accessToken - api key for the Checkly API
|
* @param {String} obj.accessToken - api key for the Checkly API
|
||||||
* @returns {Object[]} apps - Сheckly accounts
|
* @returns {Object[]} apps - Сheckly accounts
|
||||||
|
|||||||
@@ -2104,7 +2104,7 @@ const syncSecretsSupabase = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to Checkly app
|
* Sync/push [secrets] to Checkly app/group
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {IIntegration} obj.integration - integration details
|
* @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)
|
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||||
@@ -2121,93 +2121,153 @@ const syncSecretsCheckly = async ({
|
|||||||
accessToken: string;
|
accessToken: string;
|
||||||
appendices?: { prefix: string; suffix: 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(
|
if (integration.targetServiceId) {
|
||||||
(
|
// sync secrets to checkly group envars
|
||||||
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
|
let getGroupSecretsRes = (
|
||||||
for await (const key of Object.keys(secrets)) {
|
await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, {
|
||||||
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: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"X-Checkly-Account": integration.appId
|
"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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ router.get(
|
|||||||
integrationAuthController.getIntegrationAuthVercelBranches
|
integrationAuthController.getIntegrationAuthVercelBranches
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:integrationAuthId/checkly/groups",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
integrationAuthController.getIntegrationAuthChecklyGroups
|
||||||
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/qovery/orgs",
|
"/:integrationAuthId/qovery/orgs",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import Redis, { Redis as TRedis } from "ioredis";
|
import Redis, { Redis as TRedis } from "ioredis";
|
||||||
|
import { logger } from "../utils/logging";
|
||||||
|
|
||||||
let redisClient: TRedis | null;
|
let redisClient: TRedis | null;
|
||||||
|
|
||||||
if (process.env.REDIS_URL) {
|
if (process.env.REDIS_URL) {
|
||||||
redisClient = new Redis(process.env.REDIS_URL as string);
|
redisClient = new Redis(process.env.REDIS_URL as string);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Redis URL not set, skipping Redis initialization.");
|
logger.warn("Redis URL not set, skipping Redis initialization.");
|
||||||
redisClient = null;
|
redisClient = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
backfillBots,
|
backfillBots,
|
||||||
backfillEncryptionMetadata,
|
backfillEncryptionMetadata,
|
||||||
backfillIntegration,
|
backfillIntegration,
|
||||||
backfillPermission,
|
|
||||||
backfillSecretBlindIndexData,
|
backfillSecretBlindIndexData,
|
||||||
backfillSecretFolders,
|
backfillSecretFolders,
|
||||||
backfillSecretVersions,
|
backfillSecretVersions,
|
||||||
@@ -28,6 +27,7 @@ import {
|
|||||||
} from "./reencryptData";
|
} from "./reencryptData";
|
||||||
import { getMongoURL, getNodeEnv, getRedisUrl, getSentryDSN } from "../../config";
|
import { getMongoURL, getNodeEnv, getRedisUrl, getSentryDSN } from "../../config";
|
||||||
import { initializePassport } from "../auth";
|
import { initializePassport } from "../auth";
|
||||||
|
import { logger } from "../logging";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare Infisical upon startup. This includes tasks like:
|
* Prepare Infisical upon startup. This includes tasks like:
|
||||||
@@ -41,7 +41,7 @@ import { initializePassport } from "../auth";
|
|||||||
*/
|
*/
|
||||||
export const setup = async () => {
|
export const setup = async () => {
|
||||||
if ((await getRedisUrl()) === undefined || (await getRedisUrl()) === "") {
|
if ((await getRedisUrl()) === undefined || (await getRedisUrl()) === "") {
|
||||||
console.error(
|
logger.error(
|
||||||
"WARNING: Redis is not yet configured. Infisical may not function as expected without it."
|
"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({
|
export const GetIntegrationAuthQoveryOrgsV1 = z.object({
|
||||||
params: z.object({
|
params: z.object({
|
||||||
integrationAuthId: z.string().trim()
|
integrationAuthId: z.string().trim()
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.8 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -34,6 +34,13 @@ Press on the Checkly tile and input your Checkly API Key to grant Infisical acce
|
|||||||
Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets.
|
Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Infisical integrates with Checkly's environment variables at the **global** and **group** levels.
|
||||||
|
|
||||||
|
To sync secrets to a specific group, you can select a group from the Checkly Group dropdown; otherwise, leaving it empty will sync secrets globally.
|
||||||
|
</Note>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<Info>
|
<Info>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export {
|
|||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthBitBucketWorkspaces,
|
useGetIntegrationAuthBitBucketWorkspaces,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById,
|
||||||
|
useGetIntegrationAuthChecklyGroups,
|
||||||
useGetIntegrationAuthNorthflankSecretGroups,
|
useGetIntegrationAuthNorthflankSecretGroups,
|
||||||
useGetIntegrationAuthRailwayEnvironments,
|
useGetIntegrationAuthRailwayEnvironments,
|
||||||
useGetIntegrationAuthRailwayServices,
|
useGetIntegrationAuthRailwayServices,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { workspaceKeys } from "../workspace/queries";
|
|||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
BitBucketWorkspace,
|
BitBucketWorkspace,
|
||||||
|
ChecklyGroup,
|
||||||
Environment,
|
Environment,
|
||||||
IntegrationAuth,
|
IntegrationAuth,
|
||||||
NorthflankSecretGroup,
|
NorthflankSecretGroup,
|
||||||
@@ -13,7 +14,8 @@ import {
|
|||||||
Project,
|
Project,
|
||||||
Service,
|
Service,
|
||||||
Team,
|
Team,
|
||||||
TeamCityBuildConfig} from "./types";
|
TeamCityBuildConfig
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
const integrationAuthKeys = {
|
const integrationAuthKeys = {
|
||||||
getIntegrationAuthById: (integrationAuthId: string) =>
|
getIntegrationAuthById: (integrationAuthId: string) =>
|
||||||
@@ -29,6 +31,14 @@ const integrationAuthKeys = {
|
|||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
}) => [{ integrationAuthId, appId }, "integrationAuthVercelBranches"] as const,
|
}) => [{ integrationAuthId, appId }, "integrationAuthVercelBranches"] as const,
|
||||||
|
getIntegrationAuthChecklyGroups: ({
|
||||||
|
integrationAuthId,
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
accountId: string;
|
||||||
|
}) =>
|
||||||
|
[{ integrationAuthId, accountId }, "integrationAuthChecklyGroups"] as const,
|
||||||
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
|
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
|
||||||
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
|
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
|
||||||
getIntegrationAuthQoveryProjects: ({
|
getIntegrationAuthQoveryProjects: ({
|
||||||
@@ -125,6 +135,24 @@ const fetchIntegrationAuthTeams = async (integrationAuthId: string) => {
|
|||||||
return data.teams;
|
return data.teams;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchIntegrationAuthChecklyGroups = async ({
|
||||||
|
integrationAuthId,
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
accountId: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await apiRequest.get<{ groups: ChecklyGroup[] }>(
|
||||||
|
`/api/v1/integration-auth/${integrationAuthId}/checkly/groups`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
accountId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.groups;
|
||||||
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthVercelBranches = async ({
|
const fetchIntegrationAuthVercelBranches = async ({
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
@@ -413,6 +441,26 @@ export const useGetIntegrationAuthVercelBranches = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetIntegrationAuthChecklyGroups = ({
|
||||||
|
integrationAuthId,
|
||||||
|
accountId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
accountId: string;
|
||||||
|
}) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: integrationAuthKeys.getIntegrationAuthChecklyGroups({
|
||||||
|
integrationAuthId,
|
||||||
|
accountId
|
||||||
|
}),
|
||||||
|
queryFn: () => fetchIntegrationAuthChecklyGroups({
|
||||||
|
integrationAuthId,
|
||||||
|
accountId
|
||||||
|
}),
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => {
|
export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthQoveryOrgs(integrationAuthId),
|
queryKey: integrationAuthKeys.getIntegrationAuthQoveryOrgs(integrationAuthId),
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ export type Environment = {
|
|||||||
environmentId: string;
|
environmentId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChecklyGroup = {
|
||||||
|
name: string;
|
||||||
|
groupId: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type Container = {
|
export type Container = {
|
||||||
name: string;
|
name: string;
|
||||||
containerId: string;
|
containerId: string;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Head from "next/head";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen, faBugs, faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
@@ -27,7 +27,8 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthById
|
useGetIntegrationAuthById,
|
||||||
|
useGetIntegrationAuthChecklyGroups
|
||||||
} from "../../../hooks/api/integrationAuth";
|
} from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
@@ -42,20 +43,24 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
|
|
||||||
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
||||||
|
const [secretPath, setSecretPath] = useState("/");
|
||||||
|
const [secretSuffix, setSecretSuffix] = useState("");
|
||||||
|
|
||||||
|
const [targetAppId, setTargetAppId] = useState("");
|
||||||
|
const [targetGroupId, setTargetGroupId] = useState("");
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
||||||
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
||||||
const { data: integrationAuthApps, isLoading: isIntegrationAuthAppsLoading } = useGetIntegrationAuthApps({
|
const { data: integrationAuthApps, isLoading: isIntegrationAuthAppsLoading } = useGetIntegrationAuthApps({
|
||||||
integrationAuthId: (integrationAuthId as string) ?? ""
|
integrationAuthId: (integrationAuthId as string) ?? ""
|
||||||
});
|
});
|
||||||
|
const { data: integrationAuthGroups, isLoading: isintegrationAuthGroupsLoading } = useGetIntegrationAuthChecklyGroups({
|
||||||
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
const [secretPath, setSecretPath] = useState("/");
|
accountId: targetAppId
|
||||||
const [secretSuffix, setSecretSuffix] = useState("");
|
});
|
||||||
|
|
||||||
const [targetApp, setTargetApp] = useState("");
|
|
||||||
const [targetAppId, setTargetAppId] = useState("");
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
@@ -64,13 +69,11 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// TODO: handle case where apps can be empty
|
|
||||||
if (integrationAuthApps) {
|
if (integrationAuthApps) {
|
||||||
if (integrationAuthApps.length > 0) {
|
if (integrationAuthApps.length > 0) {
|
||||||
setTargetApp(integrationAuthApps[0].name);
|
setTargetAppId(integrationAuthApps[0].appId as string);
|
||||||
setTargetAppId(String(integrationAuthApps[0].appId));
|
|
||||||
} else {
|
} else {
|
||||||
setTargetApp("none");
|
setTargetAppId("none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [integrationAuthApps]);
|
}, [integrationAuthApps]);
|
||||||
@@ -81,12 +84,23 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const targetApp = integrationAuthApps?.find(
|
||||||
|
(integrationAuthApp) => integrationAuthApp.appId === targetAppId
|
||||||
|
);
|
||||||
|
const targetGroup = integrationAuthGroups?.find(
|
||||||
|
(group) => group.groupId === Number(targetGroupId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetApp) return;
|
||||||
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
integrationAuthId: integrationAuth?._id,
|
integrationAuthId: integrationAuth?._id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
app: targetApp,
|
app: targetApp?.name,
|
||||||
appId: targetAppId,
|
appId: targetApp?.appId,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
|
targetService: targetGroup?.name,
|
||||||
|
targetServiceId: String(targetGroup?.groupId),
|
||||||
secretPath,
|
secretPath,
|
||||||
metadata: {
|
metadata: {
|
||||||
secretSuffix
|
secretSuffix
|
||||||
@@ -105,8 +119,9 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
workspace &&
|
workspace &&
|
||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps &&
|
integrationAuthApps &&
|
||||||
targetApp ? (
|
integrationAuthGroups &&
|
||||||
<div className="flex flex-col h-full w-full items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900">
|
targetAppId ? (
|
||||||
|
<div className="flex h-full flex-col w-full py-6 items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900">
|
||||||
<Head>
|
<Head>
|
||||||
<title>Set Up Checkly Integration</title>
|
<title>Set Up Checkly Integration</title>
|
||||||
<link rel='icon' href='/infisical.ico' />
|
<link rel='icon' href='/infisical.ico' />
|
||||||
@@ -177,16 +192,16 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl label="Checkly Account">
|
<FormControl label="Checkly Account">
|
||||||
<Select
|
<Select
|
||||||
value={targetApp}
|
value={targetAppId}
|
||||||
onValueChange={(val) => setTargetApp(val)}
|
onValueChange={(val) => setTargetAppId(val)}
|
||||||
className="w-full border border-mineshaft-500"
|
className="w-full border border-mineshaft-500"
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
>
|
>
|
||||||
{integrationAuthApps.length > 0 ? (
|
{integrationAuthApps.length > 0 ? (
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
integrationAuthApps.map((integrationAuthApp) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
value={integrationAuthApp.name}
|
value={integrationAuthApp.appId as string}
|
||||||
key={`target-app-${integrationAuthApp.name}`}
|
key={`target-app-${integrationAuthApp.appId as string}`}
|
||||||
>
|
>
|
||||||
{integrationAuthApp.name}
|
{integrationAuthApp.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -198,6 +213,28 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl label="Checkly Group (Optional)">
|
||||||
|
<Select
|
||||||
|
value={targetGroupId}
|
||||||
|
onValueChange={(val) => setTargetGroupId(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
>
|
||||||
|
{integrationAuthGroups.length > 0 ? (
|
||||||
|
integrationAuthGroups.map((integrationAuthGroup) => (
|
||||||
|
<SelectItem
|
||||||
|
value={String(integrationAuthGroup.groupId)}
|
||||||
|
key={`target-group-${String(integrationAuthGroup.groupId)}`}
|
||||||
|
>
|
||||||
|
{integrationAuthGroup.name}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem value="none" key="target-group-none">
|
||||||
|
No groups found
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={TabSections.Options}>
|
<TabPanel value={TabSections.Options}>
|
||||||
@@ -229,12 +266,6 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
Create Integration
|
Create Integration
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
|
|
||||||
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
|
|
||||||
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
|
|
||||||
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in Checkly with secrets from Infisical.</span>
|
|
||||||
<span className="text-mineshaft-300 text-sm mt-4">If you have multiple Checkly integrations and are using suffixes for at least one of them, you will have to add suffixes for all the active Checkly integrations – otherwise you might run into rare unexpected behavior.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
<div className="flex justify-center items-center w-full h-full">
|
||||||
@@ -242,7 +273,7 @@ export default function ChecklyCreateIntegrationPage() {
|
|||||||
<title>Set Up Checkly Integration</title>
|
<title>Set Up Checkly Integration</title>
|
||||||
<link rel='icon' href='/infisical.ico' />
|
<link rel='icon' href='/infisical.ico' />
|
||||||
</Head>
|
</Head>
|
||||||
{isIntegrationAuthAppsLoading ? <img src="/images/loading/loading.gif" height={70} width={120} alt="infisical loading indicator" /> : <div className="max-w-md h-max p-6 border border-mineshaft-600 rounded-md bg-mineshaft-800 text-mineshaft-200 flex flex-col text-center">
|
{isIntegrationAuthAppsLoading || isintegrationAuthGroupsLoading ? <img src="/images/loading/loading.gif" height={70} width={120} alt="infisical loading indicator" /> : <div className="max-w-md h-max p-6 border border-mineshaft-600 rounded-md bg-mineshaft-800 text-mineshaft-200 flex flex-col text-center">
|
||||||
<FontAwesomeIcon icon={faBugs} className="text-6xl my-2 inlineli"/>
|
<FontAwesomeIcon icon={faBugs} className="text-6xl my-2 inlineli"/>
|
||||||
<p>
|
<p>
|
||||||
Something went wrong. Please contact <a
|
Something went wrong. Please contact <a
|
||||||
|
|||||||
+15
-5
@@ -163,12 +163,22 @@ export const IntegrationsSection = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{((integration.integration === "checkly") || (integration.integration === "github")) && (
|
{((integration.integration === "checkly") || (integration.integration === "github")) && (
|
||||||
<div className="ml-2 flex flex-col">
|
<>
|
||||||
<FormLabel label="Secret Suffix" />
|
{integration.targetService && (
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<div className="ml-2">
|
||||||
{integration?.metadata?.secretSuffix || "-"}
|
<FormLabel label="Group" />
|
||||||
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
|
{integration.targetService}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="ml-2">
|
||||||
|
<FormLabel label="Secret Suffix" />
|
||||||
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
|
{integration?.metadata?.secretSuffix || "-"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex cursor-default items-center">
|
<div className="flex cursor-default items-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user