diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index 11cbd6f643..4d6f6b37d5 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -8,7 +8,11 @@ import { } from '../../models'; import { INTEGRATION_SET, INTEGRATION_OPTIONS } from '../../variables'; import { IntegrationService } from '../../services'; -import { getApps, revokeAccess } from '../../integrations'; +import { + getApps, + getTeams, + revokeAccess +} from '../../integrations'; /*** * Return integration authorization with id [integrationAuthId] @@ -154,25 +158,54 @@ export const saveIntegrationAccessToken = async ( * @returns */ export const getIntegrationAuthApps = async (req: Request, res: Response) => { - let apps; - try { - apps = await getApps({ - integrationAuth: req.integrationAuth, - accessToken: req.accessToken, - }); - } catch (err) { - Sentry.setUser({ email: req.user.email }); - Sentry.captureException(err); - return res.status(400).send({ - message: "Failed to get integration authorization applications", - }); - } + let apps; + try { + const teamId = req.query.teamId as string; - return res.status(200).send({ - apps, - }); + apps = await getApps({ + integrationAuth: req.integrationAuth, + accessToken: req.accessToken, + ...teamId && { teamId } + }); + } catch (err) { + Sentry.setUser({ email: req.user.email }); + Sentry.captureException(err); + return res.status(400).send({ + message: "Failed to get integration authorization applications", + }); + } + + return res.status(200).send({ + apps + }); }; +/** + * Return list of teams allowed for integration with integration authorization id [integrationAuthId] + * @param req + * @param res + * @returns + */ +export const getIntegrationAuthTeams = async (req: Request, res: Response) => { + let teams; + try { + teams = await getTeams({ + integrationAuth: req.integrationAuth, + accessToken: req.accessToken + }); + } catch (err) { + Sentry.setUser({ email: req.user.email }); + Sentry.captureException(err); + return res.status(400).send({ + message: "Failed to get integration authorization teams" + }); + } + + return res.status(200).send({ + teams + }); +} + /** * Delete integration authorization with id [integrationAuthId] * @param req diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index 2bb666119d..521c51a2f0 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -24,22 +24,27 @@ import { INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, } from "../variables"; +import { requireIntegrationAuthorizationAuth } from "../middleware"; /** * Return list of names of apps for integration named [integration] * @param {Object} obj * @param {String} obj.integration - name of integration * @param {String} obj.accessToken - access token for integration + * @param {String} obj.teamId - (optional) id of team for getting integration apps (used for integrations like GitLab) * @returns {Object[]} apps - names of integration apps * @returns {String} apps.name - name of integration app */ const getApps = async ({ integrationAuth, accessToken, + teamId }: { integrationAuth: IIntegrationAuth; accessToken: string; + teamId?: string; }) => { + interface App { name: string; appId?: string; diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 3cabd4f9ee..45ad74370e 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -13,6 +13,7 @@ import { INTEGRATION_NETLIFY_TOKEN_URL, INTEGRATION_GITHUB_TOKEN_URL, INTEGRATION_GITLAB_TOKEN_URL, + INTEGRATION_GITLAB_API_URL } from '../variables'; import { SITE_URL, @@ -73,7 +74,7 @@ interface ExchangeCodeGithubResponse { interface ExchangeCodeGitlabResponse { access_token: string; token_type: string; - expires_in: string; + expires_in: number; refresh_token: string; scope: string; created_at: number; @@ -370,6 +371,7 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { */ const exchangeCodeGitlab = async ({ code }: { code: string }) => { let res: ExchangeCodeGitlabResponse; + const accessExpiresAt = new Date(); try { res = ( @@ -389,6 +391,46 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => { } ) ).data; + + // 1. try getting groups + // https://gitlab.com/api/v4/groups + + // const res2 = (await request.get( + // `${INTEGRATION_GITLAB_API_URL}/v4/groups`, + // { + // headers: { + // Authorization: `Bearer ${res.access_token}`, + // "Accept-Encoding": "application/json" + // } + // } + // )).data; + + // 2. try getting projects of that group + // const res3 = (await request.get( + // `${INTEGRATION_GITLAB_API_URL}/v4/groups/${res2[0].id}`, + // { + // headers: { + // Authorization: `Bearer ${res.access_token}`, + // "Accept-Encoding": "application/json" + // } + // } + // )).data; + + // const res = ( + // await request.get( + // `${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`, + // { + // headers: { + // "Authorization": `Bearer ${accessToken}`, + // "Accept-Encoding": "application/json", + // }, + // } + // ) + // ).data; + + accessExpiresAt.setSeconds( + accessExpiresAt.getSeconds() + res.expires_in + ); }catch (err) { Sentry.setUser(null); Sentry.captureException(err); @@ -397,8 +439,8 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => { return { accessToken: res.access_token, - refreshToken: null, - accessExpiresAt: null + refreshToken: res.refresh_token, + accessExpiresAt }; } diff --git a/backend/src/integrations/index.ts b/backend/src/integrations/index.ts index 86c22de0cf..8439ff3ba2 100644 --- a/backend/src/integrations/index.ts +++ b/backend/src/integrations/index.ts @@ -1,6 +1,7 @@ import { exchangeCode } from './exchange'; import { exchangeRefresh } from './refresh'; import { getApps } from './apps'; +import { getTeams } from './teams'; import { syncSecrets } from './sync'; import { revokeAccess } from './revoke'; @@ -8,6 +9,7 @@ export { exchangeCode, exchangeRefresh, getApps, + getTeams, syncSecrets, revokeAccess } \ No newline at end of file diff --git a/backend/src/integrations/teams.ts b/backend/src/integrations/teams.ts new file mode 100644 index 0000000000..41040aee56 --- /dev/null +++ b/backend/src/integrations/teams.ts @@ -0,0 +1,74 @@ +import * as Sentry from "@sentry/node"; +import { + IIntegrationAuth +} from '../models'; +import { + INTEGRATION_GITLAB, + INTEGRATION_GITLAB_API_URL +} from '../variables'; +import request from '../config/request'; + +const getTeams = async ({ + integrationAuth, + accessToken +}: { + integrationAuth: IIntegrationAuth; + accessToken: string; +}) => { + interface Team { + name: string; + teamId: string; + } + + let teams: Team[] = []; + try { + switch (integrationAuth.integration) { + case INTEGRATION_GITLAB: + teams = await getTeamsGitLab({ + accessToken + }); + break; + } + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error('Failed to get integration teams'); + } + + + return teams; +} + +const getTeamsGitLab = async ({ + accessToken +}: { + accessToken: string; +}) => { + let teams = []; + try { + const res = (await request.get( + `${INTEGRATION_GITLAB_API_URL}/v4/groups`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } + } + )).data; + + teams = res.map((t: any) => ({ + name: t.name, + teamId: t.id + })); + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error("Failed to get GitLab integration teams"); + } + + return teams; +} + +export { + getTeams +} \ No newline at end of file diff --git a/backend/src/models/integrationAuth.ts b/backend/src/models/integrationAuth.ts index 8f4aed6633..43bef63ad8 100644 --- a/backend/src/models/integrationAuth.ts +++ b/backend/src/models/integrationAuth.ts @@ -23,9 +23,9 @@ export interface IIntegrationAuth { refreshCiphertext?: string; refreshIV?: string; refreshTag?: string; - accessIdCiphertext?: string; // new - accessIdIV?: string; // new - accessIdTag?: string; // new + accessIdCiphertext?: string; + accessIdIV?: string; + accessIdTag?: string; accessCiphertext?: string; accessIV?: string; accessTag?: string; diff --git a/backend/src/routes/v1/integrationAuth.ts b/backend/src/routes/v1/integrationAuth.ts index 8d960e03c5..409422e93c 100644 --- a/backend/src/routes/v1/integrationAuth.ts +++ b/backend/src/routes/v1/integrationAuth.ts @@ -1,6 +1,6 @@ import express from 'express'; const router = express.Router(); -import { body, param } from 'express-validator'; +import { body, param, query } from 'express-validator'; import { requireAuth, requireWorkspaceAuth, @@ -64,6 +64,9 @@ router.post( integrationAuthController.saveIntegrationAccessToken ); +// this can optionally accept a teamId? +// IF teamId is passed in then it probably means that we want to get +// the apps for that team router.get( '/:integrationAuthId/apps', requireAuth({ @@ -73,10 +76,24 @@ router.get( acceptedRoles: [ADMIN, MEMBER] }), param('integrationAuthId'), + query('entity'), validateRequest, integrationAuthController.getIntegrationAuthApps ); +router.get( + '/:integrationAuthId/teams', + requireAuth({ + acceptedAuthModes: ['jwt'] + }), + requireIntegrationAuthorizationAuth({ + acceptedRoles: [ADMIN, MEMBER] + }), + param('integrationAuthId'), + validateRequest, + integrationAuthController.getIntegrationAuthTeams +); + router.delete( '/:integrationAuthId', requireAuth({ diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index d0280fbff1..5ed93a597f 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -59,6 +59,7 @@ const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql"; const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api"; const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com"; +// TODO: deprecate types? const INTEGRATION_OPTIONS = [ { name: 'Heroku', @@ -156,7 +157,7 @@ const INTEGRATION_OPTIONS = [ slug: 'gitlab', image: 'GitLab.png', isAvailable: true, - type: 'oauth', + type: 'custom', clientId: CLIENT_ID_GITLAB, docsLink: '' }, diff --git a/frontend/src/hooks/api/integrationAuth/index.tsx b/frontend/src/hooks/api/integrationAuth/index.tsx index 43920b65fa..c02938985d 100644 --- a/frontend/src/hooks/api/integrationAuth/index.tsx +++ b/frontend/src/hooks/api/integrationAuth/index.tsx @@ -1,3 +1,4 @@ export { useGetIntegrationAuthApps, - useGetIntegrationAuthById} from './queries'; \ No newline at end of file + useGetIntegrationAuthById, + useGetIntegrationAuthTeams} from './queries'; \ No newline at end of file diff --git a/frontend/src/hooks/api/integrationAuth/queries.tsx b/frontend/src/hooks/api/integrationAuth/queries.tsx index 508afc7417..e60436f993 100644 --- a/frontend/src/hooks/api/integrationAuth/queries.tsx +++ b/frontend/src/hooks/api/integrationAuth/queries.tsx @@ -4,11 +4,13 @@ import { apiRequest } from "@app/config/request"; import { App, - IntegrationAuth} from './types'; + IntegrationAuth, + Team} from './types'; const integrationAuthKeys = { getIntegrationAuthById: (integrationAuthId: string) => [{ integrationAuthId }, 'integrationAuth'] as const, - getIntegrationAuthApps: (integrationAuthId: string) => [{ integrationAuthId }, 'integrationAuthApps'] as const, + getIntegrationAuthApps: (integrationAuthId: string, teamId?: string) => [{ integrationAuthId, teamId }, 'integrationAuthApps'] as const, + getIntegrationAuthTeams: (integrationAuthId: string) => [{ integrationAuthId }, 'integrationAuthTeams'] as const } const fetchIntegrationAuthById = async (integrationAuthId: string) => { @@ -16,11 +18,27 @@ const fetchIntegrationAuthById = async (integrationAuthId: string) => { return data.integrationAuth; } -const fetchIntegrationAuthApps = async (integrationAuthId: string) => { - const { data } = await apiRequest.get<{ apps: App[] }>(`/api/v1/integration-auth/${integrationAuthId}/apps`); +const fetchIntegrationAuthApps = async ({ + integrationAuthId, + teamId +}: { + integrationAuthId: string; + teamId?: string; +}) => { + console.log('fetchIntegrationAuthApps: ', integrationAuthId); + const searchParams = new URLSearchParams(teamId ? { teamId } : undefined); + const { data } = await apiRequest.get<{ apps: App[] }>( + `/api/v1/integration-auth/${integrationAuthId}/apps`, + { params: searchParams } + ); return data.apps; } +const fetchIntegrationAuthTeams = async (integrationAuthId: string) => { + const { data } = await apiRequest.get<{ teams: Team[] }>(`/api/v1/integration-auth/${integrationAuthId}/teams`); + return data.teams; +} + export const useGetIntegrationAuthById = (integrationAuthId: string) => { return useQuery({ queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId), @@ -29,10 +47,28 @@ export const useGetIntegrationAuthById = (integrationAuthId: string) => { }); } -export const useGetIntegrationAuthApps = (integrationAuthId: string) => { +// TODO: fix to teamId +export const useGetIntegrationAuthApps = ({ + integrationAuthId, + teamId +}: { + integrationAuthId: string; + teamId?: string; +}) => { return useQuery({ - queryKey: integrationAuthKeys.getIntegrationAuthApps(integrationAuthId), - queryFn: () => fetchIntegrationAuthApps(integrationAuthId), + queryKey: integrationAuthKeys.getIntegrationAuthApps(integrationAuthId, teamId), + queryFn: () => fetchIntegrationAuthApps({ + integrationAuthId, + teamId + }), + enabled: true + }); +} + +export const useGetIntegrationAuthTeams = (integrationAuthId: string) => { + return useQuery({ + queryKey: integrationAuthKeys.getIntegrationAuthTeams(integrationAuthId), + queryFn: () => fetchIntegrationAuthTeams(integrationAuthId), enabled: true }); } \ No newline at end of file diff --git a/frontend/src/hooks/api/integrationAuth/types.ts b/frontend/src/hooks/api/integrationAuth/types.ts index bf193eafe2..4ba19c1929 100644 --- a/frontend/src/hooks/api/integrationAuth/types.ts +++ b/frontend/src/hooks/api/integrationAuth/types.ts @@ -10,4 +10,9 @@ export type App = { name: string; appId?: string; owner?: string; +} + +export type Team = { + name: string; + teamId: string; } \ No newline at end of file diff --git a/frontend/src/pages/integrations/[id].tsx b/frontend/src/pages/integrations/[id].tsx index 0305febad2..c3c625a797 100644 --- a/frontend/src/pages/integrations/[id].tsx +++ b/frontend/src/pages/integrations/[id].tsx @@ -196,16 +196,16 @@ export default function Integrations() { link = `https://gitlab.com/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`; break; case 'render': - link = `${window.location.origin}/integrations/render/authorize` + link = `${window.location.origin}/integrations/render/authorize`; break; case 'flyio': - link = `${window.location.origin}/integrations/flyio/authorize` + link = `${window.location.origin}/integrations/flyio/authorize`; break; case 'circleci': - link = `${window.location.origin}/integrations/circleci/authorize` + link = `${window.location.origin}/integrations/circleci/authorize`; break; case 'travisci': - link = `${window.location.origin}/integrations/travisci/authorize` + link = `${window.location.origin}/integrations/travisci/authorize`; break; default: break; diff --git a/frontend/src/pages/integrations/circleci/create.tsx b/frontend/src/pages/integrations/circleci/create.tsx index 8e1416dd77..b753e7dd70 100644 --- a/frontend/src/pages/integrations/circleci/create.tsx +++ b/frontend/src/pages/integrations/circleci/create.tsx @@ -22,7 +22,9 @@ export default function CircleCICreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -36,9 +38,12 @@ export default function CircleCICreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty if (integrationAuthApps) { - setTargetApp(integrationAuthApps[0]?.name); + if (integrationAuthApps.length > 0 ) { + setTargetApp(integrationAuthApps[0]?.name); + } else { + setTargetApp('none'); + } } }, [integrationAuthApps]); @@ -84,7 +89,7 @@ export default function CircleCICreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -98,19 +103,27 @@ export default function CircleCICreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No projects found - ))} + )} diff --git a/frontend/src/pages/integrations/flyio/create.tsx b/frontend/src/pages/integrations/flyio/create.tsx index 2c971065fc..05b37affd4 100644 --- a/frontend/src/pages/integrations/flyio/create.tsx +++ b/frontend/src/pages/integrations/flyio/create.tsx @@ -22,7 +22,9 @@ export default function FlyioCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -31,14 +33,18 @@ export default function FlyioCreateIntegrationPage() { useEffect(() => { if (workspace) { - setSelectedSourceEnvironment(workspace.environments[0].slug); + setSelectedSourceEnvironment(workspace.environments[0].slug); } }, [workspace]); useEffect(() => { // TODO: handle case where apps can be empty if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp('none'); + } } }, [integrationAuthApps]); @@ -84,7 +90,7 @@ export default function FlyioCreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -98,12 +104,19 @@ export default function FlyioCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No apps found - ))} + )} diff --git a/frontend/src/pages/integrations/github/create.tsx b/frontend/src/pages/integrations/github/create.tsx index 8de610fa93..5fbd8f780f 100644 --- a/frontend/src/pages/integrations/github/create.tsx +++ b/frontend/src/pages/integrations/github/create.tsx @@ -22,7 +22,9 @@ export default function GitHubCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [owner, setOwner] = useState(null); @@ -37,10 +39,13 @@ export default function GitHubCreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { setTargetApp(integrationAuthApps[0].name); setOwner(integrationAuthApps[0]?.owner ?? null); + } else { + setTargetApp('none'); + } } }, [integrationAuthApps]); @@ -99,21 +104,29 @@ export default function GitHubCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No repositories found - ))} + )} diff --git a/frontend/src/pages/integrations/gitlab/create.tsx b/frontend/src/pages/integrations/gitlab/create.tsx index f1b747ba71..9bd7d4ac19 100644 --- a/frontend/src/pages/integrations/gitlab/create.tsx +++ b/frontend/src/pages/integrations/gitlab/create.tsx @@ -11,10 +11,20 @@ import { Select, SelectItem } from '../../../components/v2'; -import { useGetIntegrationAuthApps,useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth'; +import { + useGetIntegrationAuthApps, + useGetIntegrationAuthById, + useGetIntegrationAuthTeams} from '../../../hooks/api/integrationAuth'; import { useGetWorkspaceById } from '../../../hooks/api/workspace'; import createIntegration from "../../api/integrations/createIntegration"; +const gitLabEntities = [ + { name: 'Individual', value: 'individual' }, + { name: 'Group', value: 'group' } +] + +// TODO: flesh out the data flow... + export default function GitLabCreateIntegrationPage() { const router = useRouter(); @@ -22,10 +32,18 @@ export default function GitLabCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + + const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value); + const [targetTeam, setTargetTeam] = useState('aa'); // ? + const [targetTeamId] = useState(undefined); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '', + ...(targetTeamId ? { teamId: targetTeamId } : {}) + }); + const { data: integrationAuthTeams } = useGetIntegrationAuthTeams(integrationAuthId as string ?? ''); + console.log('integrationAuthTeams: ', integrationAuthTeams); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); - const [owner, setOwner] = useState(null); const [targetApp, setTargetApp] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -38,12 +56,36 @@ export default function GitLabCreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty - if (integrationAuthApps) { - setTargetApp(integrationAuthApps[0].name); - setOwner(integrationAuthApps[0]?.owner ?? null); + if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { + console.log('AA'); + setTargetApp(integrationAuthApps[0].name); + } else { + console.log('BB'); + setTargetApp('none'); } + } }, [integrationAuthApps]); + + useEffect(() => { + // if (targetEntity === 'group' && integrationAuthTeams) { + // if (integrationAuthTeams.length > 0) { + // setTargetTeam(integrationAuthTeams[0].name); + // } else { + // setTargetTeam('none'); + // } + // } + + // if (targetEntity === 'group') { + // if (integrationAuthTeams && integrationAuthTeams.length > 0) { + // setTargetTeamId(integrationAuthTeams[0].teamId); + // } else { + // setTargetTeamId(''); + // } + // } else { + + // } + }, [integrationAuthTeams, integrationAuthApps, targetEntity]); const handleButtonClick = async () => { try { @@ -57,7 +99,7 @@ export default function GitLabCreateIntegrationPage() { appId: (integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp))?.appId ?? null, sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, - owner, + owner: null, path: null, region: null }); @@ -71,7 +113,15 @@ export default function GitLabCreateIntegrationPage() { } } - return (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && targetApp) ? ( + console.log('A', (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && integrationAuthTeams && targetApp && targetTeam)); + console.log('B', integrationAuth); + console.log('C', workspace); + console.log('D', selectedSourceEnvironment); + console.log('E', integrationAuthApps); + console.log('F', integrationAuthTeams); + console.log('G', targetApp); + console.log('H', targetTeam); + return (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && integrationAuthTeams && targetApp && targetTeam) ? (
GitLab Integration @@ -85,33 +135,83 @@ export default function GitLabCreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} + + + {targetEntity === 'group' && ( + + + + )} + diff --git a/frontend/src/pages/integrations/heroku/create.tsx b/frontend/src/pages/integrations/heroku/create.tsx index 1ba48e1486..cae8dfe44d 100644 --- a/frontend/src/pages/integrations/heroku/create.tsx +++ b/frontend/src/pages/integrations/heroku/create.tsx @@ -22,7 +22,9 @@ export default function HerokuCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -36,9 +38,12 @@ export default function HerokuCreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp('none'); + } } }, [integrationAuthApps]); @@ -83,7 +88,7 @@ export default function HerokuCreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -97,19 +102,27 @@ export default function HerokuCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No apps found - ))} + )} diff --git a/frontend/src/pages/integrations/netlify/create.tsx b/frontend/src/pages/integrations/netlify/create.tsx index 03201f6365..fb33e404b1 100644 --- a/frontend/src/pages/integrations/netlify/create.tsx +++ b/frontend/src/pages/integrations/netlify/create.tsx @@ -29,7 +29,9 @@ export default function NetlifyCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -45,10 +47,13 @@ export default function NetlifyCreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty - if (integrationAuthApps) { - setTargetApp(integrationAuthApps[0].name); + if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { + setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp('none'); } + } }, [integrationAuthApps]); const handleButtonClick = async () => { @@ -91,7 +96,7 @@ export default function NetlifyCreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -104,12 +109,19 @@ export default function NetlifyCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No sites found - ))} + )} {netlifyEnvironments.map((netlifyEnvironment) => ( - + {netlifyEnvironment.name} ))} diff --git a/frontend/src/pages/integrations/render/create.tsx b/frontend/src/pages/integrations/render/create.tsx index c8ab9d37ea..cc61eec3e2 100644 --- a/frontend/src/pages/integrations/render/create.tsx +++ b/frontend/src/pages/integrations/render/create.tsx @@ -22,7 +22,9 @@ export default function RenderCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -38,7 +40,11 @@ export default function RenderCreateIntegrationPage() { useEffect(() => { // TODO: handle case where apps can be empty if (integrationAuthApps) { - setTargetApp(integrationAuthApps[0].name); + if (integrationAuthApps.length > 0) { + setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp('none'); + } } }, [integrationAuthApps]); @@ -84,7 +90,7 @@ export default function RenderCreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -98,19 +104,27 @@ export default function RenderCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No services found - ))} + )} diff --git a/frontend/src/pages/integrations/travisci/create.tsx b/frontend/src/pages/integrations/travisci/create.tsx index 426f3b9357..c4a8a8f958 100644 --- a/frontend/src/pages/integrations/travisci/create.tsx +++ b/frontend/src/pages/integrations/travisci/create.tsx @@ -22,7 +22,9 @@ export default function TravisCICreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); @@ -36,10 +38,13 @@ export default function TravisCICreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty - if (integrationAuthApps) { - setTargetApp(integrationAuthApps[0]?.name); + if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { + setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp('none'); } + } }, [integrationAuthApps]); const handleButtonClick = async () => { @@ -84,7 +89,7 @@ export default function TravisCICreateIntegrationPage() { className='w-full border border-mineshaft-500' > {workspace?.environments.map((sourceEnvironment) => ( - + {sourceEnvironment.name} ))} @@ -98,19 +103,27 @@ export default function TravisCICreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No projects found - ))} + )} diff --git a/frontend/src/pages/integrations/vercel/create.tsx b/frontend/src/pages/integrations/vercel/create.tsx index 1805837880..6431b45976 100644 --- a/frontend/src/pages/integrations/vercel/create.tsx +++ b/frontend/src/pages/integrations/vercel/create.tsx @@ -28,11 +28,13 @@ export default function VercelCreateIntegrationPage() { const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); - const { data: integrationAuthApps } = useGetIntegrationAuthApps(integrationAuthId as string ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [targetApp, setTargetApp] = useState(''); - const [targetEnvironment, setTargetEnvironemnt] = useState(''); + const [targetEnvironment, setTargetEnvironment] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -43,10 +45,14 @@ export default function VercelCreateIntegrationPage() { }, [workspace]); useEffect(() => { - // TODO: handle case where apps can be empty if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { setTargetApp(integrationAuthApps[0].name); - setTargetEnvironemnt(vercelEnvironments[0].slug); + setTargetEnvironment(vercelEnvironments[0].slug); + } else { + setTargetApp('none'); + setTargetEnvironment(vercelEnvironments[0].slug); + } } }, [integrationAuthApps]); @@ -103,12 +109,19 @@ export default function VercelCreateIntegrationPage() { value={targetApp} onValueChange={(val) => setTargetApp(val)} className='w-full border border-mineshaft-500' + isDisabled={integrationAuthApps.length === 0} > - {integrationAuthApps.map((integrationAuthApp) => ( - - {integrationAuthApp.name} + {integrationAuthApps.length > 0 ? ( + integrationAuthApps.map((integrationAuthApp) => ( + + {integrationAuthApp.name} + + )) + ) : ( + + No projects found - ))} + )}