Merge pull request #2870 from Infisical/app-connections

Feat: App Connections
This commit is contained in:
Scott Wilson
2024-12-19 09:51:38 -08:00
committed by GitHub
113 changed files with 4380 additions and 22 deletions
@@ -0,0 +1,4 @@
export enum AppConnection {
AWS = "aws",
GitHub = "github"
}
@@ -0,0 +1,3 @@
export * from "./mutations";
export * from "./queries";
export * from "./types";
@@ -0,0 +1,58 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "@app/hooks/api/appConnections/queries";
import {
TAppConnectionResponse,
TCreateAppConnectionDTO,
TDeleteAppConnectionDTO,
TUpdateAppConnectionDTO
} from "@app/hooks/api/appConnections/types";
export const useCreateAppConnection = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ app, ...params }: TCreateAppConnectionDTO) => {
const { data } = await apiRequest.post<TAppConnectionResponse>(
`/api/v1/app-connections/${app}`,
params
);
return data.appConnection;
},
onSuccess: () => queryClient.invalidateQueries(appConnectionKeys.list())
});
};
export const useUpdateAppConnection = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ connectionId, app, ...params }: TUpdateAppConnectionDTO) => {
const { data } = await apiRequest.patch<TAppConnectionResponse>(
`/api/v1/app-connections/${app}/${connectionId}`,
params
);
return data.appConnection;
},
onSuccess: (_, { connectionId, app }) => {
queryClient.invalidateQueries(appConnectionKeys.list());
queryClient.invalidateQueries(appConnectionKeys.byId(app, connectionId));
}
});
};
export const useDeleteAppConnection = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ connectionId, app }: TDeleteAppConnectionDTO) => {
const { data } = await apiRequest.delete(`/api/v1/app-connections/${app}/${connectionId}`);
return data;
},
onSuccess: (_, { connectionId, app }) => {
queryClient.invalidateQueries(appConnectionKeys.list());
queryClient.invalidateQueries(appConnectionKeys.byId(app, connectionId));
}
});
};
@@ -0,0 +1,136 @@
import { useMemo } from "react";
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import {
TAppConnection,
TAppConnectionMap,
TAppConnectionOptions,
TGetAppConnection,
TListAppConnections
} from "@app/hooks/api/appConnections/types";
import {
TAppConnectionOption,
TAppConnectionOptionMap
} from "@app/hooks/api/appConnections/types/app-options";
export const appConnectionKeys = {
all: ["app-connection"] as const,
options: () => [...appConnectionKeys.all, "options"] as const,
list: () => [...appConnectionKeys.all, "list"] as const,
listByApp: (app: AppConnection) => [...appConnectionKeys.list(), app],
byId: (app: AppConnection, templateId: string) =>
[...appConnectionKeys.all, app, "by-id", templateId] as const
};
export const useAppConnectionOptions = (
options?: Omit<
UseQueryOptions<
TAppConnectionOption[],
unknown,
TAppConnectionOption[],
ReturnType<typeof appConnectionKeys.options>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: appConnectionKeys.options(),
queryFn: async () => {
const { data } = await apiRequest.get<TAppConnectionOptions>(
"/api/v1/app-connections/options"
);
return data.appConnectionOptions;
},
...options
});
};
export const useGetAppConnectionOption = <T extends AppConnection>(app: T) => {
const { data: options = [], isLoading } = useAppConnectionOptions();
return useMemo(
() => ({
option: (options.find((opt) => opt.app === app) as TAppConnectionOptionMap[T]) ?? {},
isLoading
}),
[options, app]
);
};
export const useListAppConnections = (
options?: Omit<
UseQueryOptions<
TAppConnection[],
unknown,
TAppConnection[],
ReturnType<typeof appConnectionKeys.list>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: appConnectionKeys.list(),
queryFn: async () => {
const { data } = await apiRequest.get<TListAppConnections<TAppConnection>>(
"/api/v1/app-connections"
);
return data.appConnections;
},
...options
});
};
export const useListAppConnectionsByApp = <T extends AppConnection>(
app: T,
options?: Omit<
UseQueryOptions<
TAppConnectionMap[T][],
unknown,
TAppConnectionMap[T][],
ReturnType<typeof appConnectionKeys.listByApp>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: appConnectionKeys.listByApp(app),
queryFn: async () => {
const { data } = await apiRequest.get<TListAppConnections<TAppConnectionMap[T]>>(
`/api/v1/app-connections/${app}`
);
return data.appConnections;
},
...options
});
};
export const useGetAppConnectionById = <T extends AppConnection>(
app: T,
connectionId: string,
options?: Omit<
UseQueryOptions<
TAppConnectionMap[T],
unknown,
TAppConnectionMap[T],
ReturnType<typeof appConnectionKeys.byId>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: appConnectionKeys.byId(app, connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<TGetAppConnection<TAppConnectionMap[T]>>(
`/api/v1/app-connections/${app}/${connectionId}`
);
return data.appConnection;
},
...options
});
};
@@ -0,0 +1,24 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
export type TAppConnectionOptionBase = {
name: string;
methods: string[];
};
export type TAwsConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.AWS;
accessKeyId?: string;
};
export type TGitHubConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.GitHub;
oauthClientId?: string;
appClientSlug?: string;
};
export type TAppConnectionOption = TAwsConnectionOption | TGitHubConnectionOption;
export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption;
[AppConnection.GitHub]: TGitHubConnectionOption;
};
@@ -0,0 +1,23 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum AwsConnectionMethod {
AssumeRole = "assume-role",
AccessKey = "access-key"
}
export type TAwsConnection = TRootAppConnection & { app: AppConnection.AWS } & (
| {
method: AwsConnectionMethod.AccessKey;
credentials: {
accessKeyId: string;
secretAccessKey: string;
};
}
| {
method: AwsConnectionMethod.AssumeRole;
credentials: {
roleArn: string;
};
}
);
@@ -0,0 +1,23 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum GitHubConnectionMethod {
App = "github-app",
OAuth = "oauth"
}
export type TGitHubConnection = TRootAppConnection & { app: AppConnection.GitHub } & (
| {
method: GitHubConnectionMethod.OAuth;
credentials: {
code: string;
};
}
| {
method: GitHubConnectionMethod.App;
credentials: {
code: string;
installationId: string;
};
}
);
@@ -0,0 +1,36 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TAppConnectionOption } from "@app/hooks/api/appConnections/types/app-options";
import { TAwsConnection } from "@app/hooks/api/appConnections/types/aws-connection";
import { TGitHubConnection } from "@app/hooks/api/appConnections/types/github-connection";
export * from "./aws-connection";
export * from "./github-connection";
export type TAppConnection = TAwsConnection | TGitHubConnection;
export type TListAppConnections<T extends TAppConnection> = { appConnections: T[] };
export type TGetAppConnection<T extends TAppConnection> = { appConnection: T };
export type TAppConnectionOptions = { appConnectionOptions: TAppConnectionOption[] };
export type TAppConnectionResponse = { appConnection: TAppConnection };
export type TCreateAppConnectionDTO = Pick<
TAppConnection,
"name" | "credentials" | "method" | "app" | "description"
>;
export type TUpdateAppConnectionDTO = Partial<
Pick<TAppConnection, "name" | "credentials" | "description">
> & {
connectionId: string;
app: AppConnection;
};
export type TDeleteAppConnectionDTO = {
app: AppConnection;
connectionId: string;
};
export type TAppConnectionMap = {
[AppConnection.AWS]: TAwsConnection;
[AppConnection.GitHub]: TGitHubConnection;
};
@@ -0,0 +1,9 @@
export type TRootAppConnection = {
id: string;
name: string;
description?: string | null;
version: number;
orgId: string;
createdAt: string;
updatedAt: string;
};
@@ -45,4 +45,5 @@ export type SubscriptionPlan = {
pkiEst: boolean;
enforceMfa: boolean;
projectTemplates: boolean;
appConnections: boolean; // TODO: remove once released
};