- Add a max iteration to loop - Hide gateways on frontend if license

does not allow them - Fix capitalization issue with GitHub secret sync
This commit is contained in:
x032205
2025-07-24 22:25:23 -04:00
parent e34deb7bd0
commit 0f06c4c27a
7 changed files with 296 additions and 266 deletions
@@ -116,6 +116,18 @@ export const getGitHubAppAuthToken = async (appConnection: TGitHubConnection) =>
return token; return token;
}; };
function extractNextPageUrl(linkHeader: string | undefined): string | null {
if (!linkHeader) return null;
const links = linkHeader.split(",");
const nextLink = links.find((link) => link.includes('rel="next"'));
if (!nextLink) return null;
const match = new RE2(/<([^>]+)>/).exec(nextLink);
return match ? match[1] : null;
}
export const makePaginatedGitHubRequest = async <T, R = T[]>( export const makePaginatedGitHubRequest = async <T, R = T[]>(
appConnection: TGitHubConnection, appConnection: TGitHubConnection,
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">, gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
@@ -128,8 +140,9 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
method === GitHubConnectionMethod.OAuth ? credentials.accessToken : await getGitHubAppAuthToken(appConnection); method === GitHubConnectionMethod.OAuth ? credentials.accessToken : await getGitHubAppAuthToken(appConnection);
let url: string | null = `https://api.${credentials.host || "github.com"}${path}`; let url: string | null = `https://api.${credentials.host || "github.com"}${path}`;
let results: T[] = []; let results: T[] = [];
let i = 0;
while (url) { while (url && i < 1000) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const response: AxiosResponse<R> = await requestWithGitHubGateway<R>(appConnection, gatewayService, { const response: AxiosResponse<R> = await requestWithGitHubGateway<R>(appConnection, gatewayService, {
url, url,
@@ -144,14 +157,8 @@ export const makePaginatedGitHubRequest = async <T, R = T[]>(
const items = dataMapper ? dataMapper(response.data) : (response.data as unknown as T[]); const items = dataMapper ? dataMapper(response.data) : (response.data as unknown as T[]);
results = results.concat(items); results = results.concat(items);
const linkHeader = response.headers.link as string | undefined; url = extractNextPageUrl(response.headers.link as string | undefined);
const nextLink = i += 1;
typeof linkHeader === "string" ? linkHeader.split(",").find((s) => s.includes('rel="next"')) : undefined;
if (nextLink) {
url = new RE2(/<(.+)>/).exec(nextLink)?.[1] || null;
} else {
url = null;
}
} }
return results; return results;
@@ -171,9 +171,11 @@ const putSecret = async (
export const GithubSyncFns = { export const GithubSyncFns = {
syncSecrets: async ( syncSecrets: async (
secretSync: TGitHubSyncWithCredentials, secretSync: TGitHubSyncWithCredentials,
secretMap: TSecretMap, ogSecretMap: TSecretMap,
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId"> gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
) => { ) => {
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
switch (secretSync.destinationConfig.scope) { switch (secretSync.destinationConfig.scope) {
case GitHubSyncScope.Organization: case GitHubSyncScope.Organization:
if (Object.values(secretMap).length > 1000) { if (Object.values(secretMap).length > 1000) {
@@ -252,9 +254,11 @@ export const GithubSyncFns = {
}, },
removeSecrets: async ( removeSecrets: async (
secretSync: TGitHubSyncWithCredentials, secretSync: TGitHubSyncWithCredentials,
secretMap: TSecretMap, ogSecretMap: TSecretMap,
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId"> gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
) => { ) => {
const secretMap = Object.fromEntries(Object.entries(ogSecretMap).map(([i, v]) => [i.toUpperCase(), v]));
const { connection } = secretSync; const { connection } = secretSync;
const token = const token =
connection.method === GitHubConnectionMethod.OAuth connection.method === GitHubConnectionMethod.OAuth
@@ -20,6 +20,7 @@ import {
SelectItem, SelectItem,
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { import {
OrgGatewayPermissionActions, OrgGatewayPermissionActions,
OrgPermissionSubjects OrgPermissionSubjects
@@ -80,6 +81,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const { subscription } = useSubscription();
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list()); const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
const selectedMethod = watch("method"); const selectedMethod = watch("method");
@@ -173,6 +175,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
</FormControl> </FormControl>
)} )}
/> />
{subscription.gateway && (
<Accordion type="single" collapsible className="w-full"> <Accordion type="single" collapsible className="w-full">
<AccordionItem value="enterprise-options" className="data-[state=open]:border-none"> <AccordionItem value="enterprise-options" className="data-[state=open]:border-none">
<AccordionTrigger className="h-fit flex-none pl-1 text-sm"> <AccordionTrigger className="h-fit flex-none pl-1 text-sm">
@@ -246,7 +249,7 @@ export const GitHubConnectionForm = ({ appConnection }: Props) => {
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
)}
<div className="mt-8 flex items-center"> <div className="mt-8 flex items-center">
<Button <Button
className="mr-4" className="mr-4"
@@ -6,6 +6,7 @@ import { z } from "zod";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2"; import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { import {
OrgGatewayPermissionActions, OrgGatewayPermissionActions,
OrgPermissionSubjects OrgPermissionSubjects
@@ -78,6 +79,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const { subscription } = useSubscription();
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false; const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list()); const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
@@ -99,6 +101,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
}} }}
> >
{!isUpdate && <GenericAppConnectionsFields />} {!isUpdate && <GenericAppConnectionsFields />}
{subscription.gateway && (
<OrgPermissionCan <OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways} I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway} a={OrgPermissionSubjects.Gateway}
@@ -148,6 +151,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
/> />
)} )}
</OrgPermissionCan> </OrgPermissionCan>
)}
<Controller <Controller
name="method" name="method"
control={control} control={control}
@@ -6,6 +6,7 @@ import { z } from "zod";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2"; import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { import {
OrgGatewayPermissionActions, OrgGatewayPermissionActions,
OrgPermissionSubjects OrgPermissionSubjects
@@ -75,6 +76,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const { subscription } = useSubscription();
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false; const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list()); const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
@@ -96,6 +98,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
}} }}
> >
{!isUpdate && <GenericAppConnectionsFields />} {!isUpdate && <GenericAppConnectionsFields />}
{subscription.gateway && (
<OrgPermissionCan <OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways} I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway} a={OrgPermissionSubjects.Gateway}
@@ -145,6 +148,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
/> />
)} )}
</OrgPermissionCan> </OrgPermissionCan>
)}
<Controller <Controller
name="method" name="method"
control={control} control={control}
@@ -6,6 +6,7 @@ import { z } from "zod";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2"; import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { import {
OrgGatewayPermissionActions, OrgGatewayPermissionActions,
OrgPermissionSubjects OrgPermissionSubjects
@@ -75,6 +76,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const { subscription } = useSubscription();
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false; const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list()); const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
@@ -96,6 +98,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
}} }}
> >
{!isUpdate && <GenericAppConnectionsFields />} {!isUpdate && <GenericAppConnectionsFields />}
{subscription.gateway && (
<OrgPermissionCan <OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways} I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway} a={OrgPermissionSubjects.Gateway}
@@ -145,6 +148,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
/> />
)} )}
</OrgPermissionCan> </OrgPermissionCan>
)}
<Controller <Controller
name="method" name="method"
control={control} control={control}
@@ -6,6 +6,7 @@ import { z } from "zod";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2"; import { Button, FormControl, ModalClose, Select, SelectItem, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { import {
OrgGatewayPermissionActions, OrgGatewayPermissionActions,
OrgPermissionSubjects OrgPermissionSubjects
@@ -75,6 +76,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const { subscription } = useSubscription();
const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false; const isPlatformManagedCredentials = appConnection?.isPlatformManagedCredentials ?? false;
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list()); const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
@@ -96,6 +98,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
}} }}
> >
{!isUpdate && <GenericAppConnectionsFields />} {!isUpdate && <GenericAppConnectionsFields />}
{subscription.gateway && (
<OrgPermissionCan <OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways} I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway} a={OrgPermissionSubjects.Gateway}
@@ -145,6 +148,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
/> />
)} )}
</OrgPermissionCan> </OrgPermissionCan>
)}
<Controller <Controller
name="method" name="method"
control={control} control={control}