mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-09 15:38:03 -05:00
feat: integrate telemetry service for secret synchronization events and update project role permissions to reflect workspace integrations
This commit is contained in:
@@ -1329,7 +1329,8 @@ export const registerRoutes = async (
|
||||
eventBusService,
|
||||
licenseService,
|
||||
membershipRoleDAL,
|
||||
membershipUserDAL
|
||||
membershipUserDAL,
|
||||
telemetryService
|
||||
});
|
||||
|
||||
const projectService = projectServiceFactory({
|
||||
|
||||
@@ -64,6 +64,8 @@ import { expandSecretReferencesFactory, getAllSecretReferences } from "../secret
|
||||
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TTelemetryServiceFactory } from "../telemetry/telemetry-service";
|
||||
import { PostHogEventTypes } from "../telemetry/telemetry-types";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TWebhookDALFactory } from "../webhook/webhook-dal";
|
||||
import { fnTriggerWebhook } from "../webhook/webhook-fns";
|
||||
@@ -120,6 +122,7 @@ type TSecretQueueFactoryDep = {
|
||||
reminderService: Pick<TReminderServiceFactory, "createReminderInternal" | "deleteReminderBySecretId">;
|
||||
eventBusService: TEventBusService;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
telemetryService: Pick<TTelemetryServiceFactory, "sendPostHogEvents">;
|
||||
};
|
||||
|
||||
export type TGetSecrets = {
|
||||
@@ -184,7 +187,8 @@ export const secretQueueFactory = ({
|
||||
eventBusService,
|
||||
licenseService,
|
||||
membershipUserDAL,
|
||||
membershipRoleDAL
|
||||
membershipRoleDAL,
|
||||
telemetryService
|
||||
}: TSecretQueueFactoryDep) => {
|
||||
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
|
||||
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
|
||||
@@ -1029,6 +1033,29 @@ export const secretQueueFactory = ({
|
||||
isSynced: response?.isSynced ?? true
|
||||
});
|
||||
|
||||
await telemetryService.sendPostHogEvents({
|
||||
event: PostHogEventTypes.IntegrationSynced,
|
||||
distinctId: `project/${projectId}`,
|
||||
organizationId: project.orgId,
|
||||
properties: {
|
||||
integrationId: integration.id,
|
||||
integration: integration.integration,
|
||||
environment,
|
||||
secretPath,
|
||||
projectId,
|
||||
url: integration.url ?? undefined,
|
||||
app: integration.app ?? undefined,
|
||||
appId: integration.appId ?? undefined,
|
||||
targetEnvironment: integration.targetEnvironment ?? undefined,
|
||||
targetEnvironmentId: integration.targetEnvironmentId ?? undefined,
|
||||
targetService: integration.targetService ?? undefined,
|
||||
targetServiceId: integration.targetServiceId ?? undefined,
|
||||
path: integration.path ?? undefined,
|
||||
region: integration.region ?? undefined,
|
||||
isManualSync: isManual ?? false
|
||||
}
|
||||
});
|
||||
|
||||
// May be undefined, if it's undefined we assume the sync was successful, hence the strict equality type check.
|
||||
if (response?.isSynced === false) {
|
||||
integrationsFailedToSync.push({
|
||||
|
||||
@@ -574,7 +574,6 @@
|
||||
"pages": [
|
||||
"integrations/cicd/aws-amplify",
|
||||
"integrations/cicd/bitbucket",
|
||||
"integrations/cicd/checkly",
|
||||
"integrations/cicd/githubactions",
|
||||
"integrations/cicd/gitlab",
|
||||
"integrations/cicd/jenkins"
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
title: "Checkly"
|
||||
description: "How to sync secrets from Infisical to Checkly"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
<Steps>
|
||||
<Step title="Authorize Infisical for Checkly">
|
||||
Obtain a Checkly API Key in User Settings > API Keys.
|
||||
|
||||

|
||||

|
||||
|
||||
Navigate to your project's integrations tab in Infisical.
|
||||
|
||||

|
||||
|
||||
Press on the Checkly tile and input your Checkly API Key to grant Infisical access to your Checkly account.
|
||||
|
||||

|
||||
|
||||
</Step>
|
||||
<Step title="Start integration">
|
||||
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>
|
||||
In the new version of the Checkly integration, you are able to specify suffixes that depend on the secrets' environment and path.
|
||||
If you choose to do so, you should utilize such suffixes for ALL Checkly integrations – otherwise the integration system
|
||||
might run into issues with deleting secrets from the wrong environments.
|
||||
</Info>
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -18,7 +18,8 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionSub, useProject } from "@app/context";
|
||||
import { useGetWorkspaceIntegrations } from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
|
||||
import {
|
||||
@@ -46,6 +47,9 @@ type TForm = { permissions: Record<ProjectPermissionSub, boolean> };
|
||||
const Content = ({ onClose, type: projectType }: ContentProps) => {
|
||||
const rootForm = useFormContext<TFormSchema>();
|
||||
const [search, setSearch] = useState("");
|
||||
const { currentProject } = useProject();
|
||||
const { data: integrations = [] } = useGetWorkspaceIntegrations(currentProject?.id ?? "");
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -60,6 +64,8 @@ const Content = ({ onClose, type: projectType }: ContentProps) => {
|
||||
}
|
||||
});
|
||||
|
||||
const hasNativeIntegrations = integrations.length > 0;
|
||||
|
||||
const filteredPolicies = Object.entries(PROJECT_PERMISSION_OBJECT)
|
||||
.filter(
|
||||
([subject, { title }]) =>
|
||||
@@ -68,6 +74,11 @@ const Content = ({ onClose, type: projectType }: ContentProps) => {
|
||||
] && (search ? title.toLowerCase().includes(search.toLowerCase()) : true)
|
||||
)
|
||||
.filter(([subject]) => !EXCLUDED_PERMISSION_SUBS.includes(subject as ProjectPermissionSub))
|
||||
.filter(
|
||||
([subject]) =>
|
||||
// Hide Native Integrations policy if project has no integrations
|
||||
subject !== ProjectPermissionSub.Integrations || hasNativeIntegrations
|
||||
)
|
||||
.sort((a, b) => a[1].title.localeCompare(b[1].title))
|
||||
.map(([subject]) => subject);
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@ import { Button } from "@app/components/v2";
|
||||
import { ProjectPermissionSub, useProject } from "@app/context";
|
||||
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
|
||||
import { evaluatePermissionsAbility } from "@app/helpers/permissions";
|
||||
import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api";
|
||||
import {
|
||||
useGetProjectRoleBySlug,
|
||||
useGetWorkspaceIntegrations,
|
||||
useUpdateProjectRole
|
||||
} from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
|
||||
@@ -105,6 +109,8 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
||||
currentProject?.id ?? "",
|
||||
roleSlug as string
|
||||
);
|
||||
const { data: integrations = [] } = useGetWorkspaceIntegrations(projectId);
|
||||
const hasNativeIntegrations = integrations.length > 0;
|
||||
|
||||
const [showAccessTree, setShowAccessTree] = useState<ProjectPermissionSub | null>(null);
|
||||
|
||||
@@ -198,6 +204,11 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
|
||||
{!isPending && <PermissionEmptyState />}
|
||||
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[])
|
||||
.filter((subject) => !EXCLUDED_PERMISSION_SUBS.includes(subject))
|
||||
.filter(
|
||||
(subject) =>
|
||||
// Hide Native Integrations policy if project has no integrations
|
||||
subject !== ProjectPermissionSub.Integrations || hasNativeIntegrations
|
||||
)
|
||||
.map((subject) => (
|
||||
<GeneralPermissionPolicies
|
||||
subject={subject}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
PageHeader,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
Tabs
|
||||
} from "@app/components/v2";
|
||||
import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
@@ -106,28 +100,33 @@ export const IntegrationsListPage = () => {
|
||||
</TabPanel>
|
||||
{hasNativeIntegrations && (
|
||||
<TabPanel value={IntegrationsListPageTabs.NativeIntegrations}>
|
||||
<Alert variant="warning" className="mb-4" hideTitle>
|
||||
<AlertDescription>
|
||||
We're moving Native Integrations to{" "}
|
||||
<a
|
||||
href="https://infisical.com/docs/integrations/secret-syncs/overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-100"
|
||||
>
|
||||
Secret Syncs
|
||||
</a>
|
||||
. If the integration you need isn't available in the Secret Syncs menu,
|
||||
please get in touch with us at{" "}
|
||||
<a
|
||||
href="mailto:team@infisical.com"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-100"
|
||||
>
|
||||
team@infisical.com
|
||||
</a>
|
||||
.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className="mb-4 flex items-start rounded-md border border-yellow-600 bg-yellow-900/20 px-3 py-2">
|
||||
<div className="flex text-sm text-yellow-100">
|
||||
<FontAwesomeIcon icon={faWarning} className="mt-1 mr-2 text-yellow-600" />
|
||||
<div>
|
||||
<p>
|
||||
We're moving Native Integrations to{" "}
|
||||
<a
|
||||
href="https://infisical.com/docs/integrations/secret-syncs/overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-100"
|
||||
>
|
||||
Secret Syncs
|
||||
</a>
|
||||
. If the integration you need isn't available in the Secret Syncs menu,
|
||||
please get in touch with us at{" "}
|
||||
<a
|
||||
href="mailto:team@infisical.com"
|
||||
className="underline underline-offset-2 hover:text-mineshaft-100"
|
||||
>
|
||||
team@infisical.com
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProjectPermissionCan
|
||||
renderGuardBanner
|
||||
I={ProjectPermissionActions.Read}
|
||||
|
||||
Reference in New Issue
Block a user