feat(folder-scoped-integrations): implemented ui for folders in integration page

This commit is contained in:
akhilmhdh
2023-06-15 22:46:40 +05:30
parent 2471418591
commit a743c12c1b
20 changed files with 417 additions and 153 deletions
@@ -4,7 +4,11 @@ import { useRouter } from 'next/router';
import { faArrowRight, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons'; import { faArrowRight, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// TODO: This needs to be moved from public folder // TODO: This needs to be moved from public folder
import { contextNetlifyMapping, integrationSlugNameMapping, reverseContextNetlifyMapping } from 'public/data/frequentConstants'; import {
contextNetlifyMapping,
integrationSlugNameMapping,
reverseContextNetlifyMapping
} from 'public/data/frequentConstants';
import Button from '@app/components/basic/buttons/Button'; import Button from '@app/components/basic/buttons/Button';
import ListBox from '@app/components/basic/Listbox'; import ListBox from '@app/components/basic/Listbox';
@@ -27,6 +31,7 @@ interface Integration {
targetEnvironment: string; targetEnvironment: string;
workspace: string; workspace: string;
integrationAuth: string; integrationAuth: string;
secretPath: string;
} }
interface IntegrationApp { interface IntegrationApp {
@@ -55,7 +60,6 @@ const IntegrationTile = ({
environments = [], environments = [],
handleDeleteIntegration handleDeleteIntegration
}: Props) => { }: Props) => {
const [integrationEnvironment, setIntegrationEnvironment] = useState<Props['environments'][0]>( const [integrationEnvironment, setIntegrationEnvironment] = useState<Props['environments'][0]>(
environments.find(({ slug }) => slug === integration?.environment) || { environments.find(({ slug }) => slug === integration?.environment) || {
name: '', name: '',
@@ -74,16 +78,16 @@ const IntegrationTile = ({
}); });
setApps(tempApps); setApps(tempApps);
if (integration?.app) { if (integration?.app) {
setIntegrationApp(integration.app); setIntegrationApp(integration.app);
} else if (integration?.path && integration?.region) { } else if (integration?.path && integration?.region) {
setIntegrationApp(`${integration.path} (${integration.region})`); setIntegrationApp(`${integration.path} (${integration.region})`);
} else if (tempApps.length > 0) { } else if (tempApps.length > 0) {
setIntegrationApp(tempApps[0].name) setIntegrationApp(tempApps[0].name);
} else { } else {
setIntegrationApp(''); setIntegrationApp('');
} }
switch (integration.integration) { switch (integration.integration) {
case 'vercel': case 'vercel':
@@ -212,12 +216,15 @@ const IntegrationTile = ({
return <div />; return <div />;
}; };
if (!integrationApp && integration.integration !== "checkly") return <div />; if (!integrationApp && integration.integration !== 'checkly') return <div />;
const isSelected = integration.integration === 'hashicorp-vault' ? `${integration.app} - path: ${integration.path}` : integrationApp; const isSelected =
integration.integration === 'hashicorp-vault'
? `${integration.app} - path: ${integration.path}`
: integrationApp;
return ( return (
<div className="mx-6 mb-8 flex max-w-5xl justify-between rounded-md bg-mineshaft-800 border border-mineshaft-600 p-6"> <div className="mx-6 mb-8 flex max-w-5xl justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6">
<div className="flex"> <div className="flex">
<div> <div>
<p className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</p> <p className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</p>
@@ -235,33 +242,46 @@ const IntegrationTile = ({
isFull isFull
/> />
</div> </div>
<div className="ml-2">
<p className="mb-2 text-xs font-semibold text-gray-400">SECRET PATH</p>
<div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
{integration.secretPath}
</div>
</div>
<div className="pt-2"> <div className="pt-2">
<FontAwesomeIcon icon={faArrowRight} className="mx-4 mt-8 text-gray-400" /> <FontAwesomeIcon icon={faArrowRight} className="mx-4 mt-8 text-gray-400" />
</div> </div>
<div className="mr-2"> <div className="mr-2">
<p className="text-gray-400 text-xs font-semibold mb-2">INTEGRATION</p> <p className="mb-2 text-xs font-semibold text-gray-400">INTEGRATION</p>
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-10 text-sm font-semibold text-gray-300 cursor-default"> <div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */} {/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
{integrationSlugNameMapping[integration.integration]} {integrationSlugNameMapping[integration.integration]}
</div> </div>
</div> </div>
<div className="mr-2"> <div className="mr-2">
<div className="mb-2 text-xs font-semibold text-gray-400">APP</div> <div className="mb-2 text-xs font-semibold text-gray-400">APP</div>
{integrationApp ? <div title={integrationApp}> {integrationApp ? (
<ListBox <div title={integrationApp}>
data={!integration.isActive ? apps.map((app) => app.name) : null} <ListBox
isSelected={isSelected} data={!integration.isActive ? apps.map((app) => app.name) : null}
onChange={(app) => { isSelected={isSelected}
setIntegrationApp(app); onChange={(app) => {
}} setIntegrationApp(app);
/> }}
</div> : <div className='w-52 h-10 rounded-md bg-mineshaft-600 animate-pulse px-4 font-bold py-2'>-</div>} />
</div>
) : (
<div className="h-10 w-52 animate-pulse rounded-md bg-mineshaft-600 px-4 py-2 font-bold">
-
</div>
)}
</div> </div>
{renderIntegrationSpecificParams(integration)} {renderIntegrationSpecificParams(integration)}
</div> </div>
<div className="flex items-end cursor-default"> <div className="flex cursor-default items-end">
{integration.isActive ? ( {integration.isActive ? (
<div className="flex max-w-5xl flex-row items-center rounded-md bg-mineshaft-600 p-[0.44rem] px-4 border border-mineshaft-500"> <div className="flex max-w-5xl flex-row items-center rounded-md border border-mineshaft-500 bg-mineshaft-600 p-[0.44rem] px-4">
<FontAwesomeIcon icon={faCheck} className="mr-2.5 text-lg text-primary" /> <FontAwesomeIcon icon={faCheck} className="mr-2.5 text-lg text-primary" />
<div className="font-semibold text-gray-300">In Sync</div> <div className="font-semibold text-gray-300">In Sync</div>
</div> </div>
@@ -23,6 +23,7 @@ interface Integration {
targetEnvironment: string; targetEnvironment: string;
workspace: string; workspace: string;
integrationAuth: string; integrationAuth: string;
secretPath: string;
} }
const ProjectIntegrationSection = ({ const ProjectIntegrationSection = ({
@@ -3,6 +3,7 @@ import SecurityClient from '@app/components/utilities/SecurityClient';
interface Props { interface Props {
integrationAuthId: string; integrationAuthId: string;
isActive: boolean; isActive: boolean;
secretPath: string;
app: string | null; app: string | null;
appId: string | null; appId: string | null;
sourceEnvironment: string; sourceEnvironment: string;
@@ -20,19 +21,20 @@ interface Props {
* @param {String} obj.accessToken - id of integration authorization for which to create the integration * @param {String} obj.accessToken - id of integration authorization for which to create the integration
* @returns * @returns
*/ */
const createIntegration = ({ const createIntegration = ({
integrationAuthId, integrationAuthId,
isActive, isActive,
app, app,
appId, appId,
sourceEnvironment, sourceEnvironment,
targetEnvironment, targetEnvironment,
targetEnvironmentId, targetEnvironmentId,
targetService, targetService,
targetServiceId, targetServiceId,
owner, owner,
path, path,
region region,
secretPath
}: Props) => }: Props) =>
SecurityClient.fetchCall('/api/v1/integration', { SecurityClient.fetchCall('/api/v1/integration', {
method: 'POST', method: 'POST',
@@ -40,18 +42,19 @@ const createIntegration = ({
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
integrationAuthId, integrationAuthId,
isActive, isActive,
app, app,
appId, appId,
sourceEnvironment, sourceEnvironment,
targetEnvironment, targetEnvironment,
targetEnvironmentId, targetEnvironmentId,
targetService, targetService,
targetServiceId, targetServiceId,
owner, owner,
path, path,
region region,
secretPath
}) })
}).then(async (res) => { }).then(async (res) => {
if (res && res.status === 200) { if (res && res.status === 200) {
@@ -61,4 +64,4 @@ const createIntegration = ({
return undefined; return undefined;
}); });
export default createIntegration; export default createIntegration;
+1
View File
@@ -44,6 +44,7 @@ interface Integration {
integration: string; integration: string;
targetEnvironment: string; targetEnvironment: string;
workspace: string; workspace: string;
secretPath:string;
integrationAuth: string; integrationAuth: string;
} }
@@ -56,6 +56,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? '');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [selectedAWSRegion, setSelectedAWSRegion] = useState(''); const [selectedAWSRegion, setSelectedAWSRegion] = useState('');
const [path, setPath] = useState(''); const [path, setPath] = useState('');
const [pathErrorText, setPathErrorText] = useState(''); const [pathErrorText, setPathErrorText] = useState('');
@@ -69,9 +70,9 @@ export default function AWSParameterStoreCreateIntegrationPage() {
} }
}, [workspace]); }, [workspace]);
const isValidAWSParameterStorePath = (secretPath: string) => { const isValidAWSParameterStorePath = (awsStorePath: string) => {
const pattern = /^\/([\w-]+\/)*[\w-]+\/$/; const pattern = /^\/([\w-]+\/)*[\w-]+\/$/;
return pattern.test(secretPath) && secretPath.length <= 2048; return pattern.test(awsStorePath) && awsStorePath.length <= 2048;
}; };
const handleButtonClick = async () => { const handleButtonClick = async () => {
@@ -101,7 +102,8 @@ export default function AWSParameterStoreCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path, path,
region: selectedAWSRegion region: selectedAWSRegion,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -133,6 +135,13 @@ export default function AWSParameterStoreCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region"> <FormControl label="AWS Region">
<Select <Select
value={selectedAWSRegion} value={selectedAWSRegion}
@@ -56,6 +56,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? '');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [selectedAWSRegion, setSelectedAWSRegion] = useState(''); const [selectedAWSRegion, setSelectedAWSRegion] = useState('');
const [targetSecretName, setTargetSecretName] = useState(''); const [targetSecretName, setTargetSecretName] = useState('');
const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState(''); const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState('');
@@ -100,7 +101,8 @@ export default function AWSSecretManagerCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: selectedAWSRegion region: selectedAWSRegion,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -132,6 +134,13 @@ export default function AWSSecretManagerCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="AWS Region"> <FormControl label="AWS Region">
<Select <Select
value={selectedAWSRegion} value={selectedAWSRegion}
@@ -24,6 +24,7 @@ export default function AzureKeyVaultCreateIntegrationPage() {
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? '');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [vaultBaseUrl, setVaultBaseUrl] = useState(''); const [vaultBaseUrl, setVaultBaseUrl] = useState('');
const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState(''); const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState('');
@@ -63,7 +64,8 @@ export default function AzureKeyVaultCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -93,6 +95,13 @@ export default function AzureKeyVaultCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl <FormControl
label="Vault URI" label="Vault URI"
errorText={vaultBaseUrlErrorText} errorText={vaultBaseUrlErrorText}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -22,6 +30,8 @@ export default function ChecklyCreateIntegrationPage() {
}); });
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [targetAppId, setTargetAppId] = useState(''); const [targetAppId, setTargetAppId] = useState('');
@@ -63,7 +73,8 @@ export default function ChecklyCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -80,8 +91,13 @@ export default function ChecklyCreateIntegrationPage() {
integrationAuthApps && integrationAuthApps &&
targetApp ? ( targetApp ? (
<div className="flex h-full w-full items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900"> <div className="flex h-full w-full items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900">
<Card className="max-w-lg rounded-md p-0 border border-mineshaft-600"> <Card className="max-w-lg rounded-md border border-mineshaft-600 p-0">
<CardTitle className="text-left px-6" subTitle="Choose which environment in Infisical you want to sync with your Checkly account.">Checkly Integration</CardTitle> <CardTitle
className="px-6 text-left"
subTitle="Choose which environment in Infisical you want to sync with your Checkly account."
>
Checkly Integration
</CardTitle>
<FormControl label="Infisical Project Environment" className="mt-2 px-6"> <FormControl label="Infisical Project Environment" className="mt-2 px-6">
<Select <Select
value={selectedSourceEnvironment} value={selectedSourceEnvironment}
@@ -98,6 +114,13 @@ export default function ChecklyCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Checkly Account" className="mt-4 px-6"> <FormControl label="Checkly Account" className="mt-4 px-6">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -22,6 +30,8 @@ export default function CircleCICreateIntegrationPage() {
}); });
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -62,7 +72,8 @@ export default function CircleCICreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -97,6 +108,13 @@ export default function CircleCICreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="CircleCI Project" className="mt-4"> <FormControl label="CircleCI Project" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -22,6 +30,8 @@ export default function FlyioCreateIntegrationPage() {
}); });
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -61,7 +71,8 @@ export default function FlyioCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -96,6 +107,13 @@ export default function FlyioCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Fly.io App" className="mt-4"> <FormControl label="Fly.io App" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -22,6 +30,7 @@ export default function GitHubCreateIntegrationPage() {
}); });
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetAppId, setTargetAppId] = useState(''); const [targetAppId, setTargetAppId] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -66,7 +75,8 @@ export default function GitHubCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: targetApp.owner, owner: targetApp.owner,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -100,6 +110,13 @@ export default function GitHubCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="GitHub Repo" className="mt-4"> <FormControl label="GitHub Repo" className="mt-4">
<Select <Select
value={targetAppId} value={targetAppId}
@@ -2,13 +2,13 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { import {
Button, Button,
Card, Card,
CardTitle, CardTitle,
FormControl, FormControl,
Input, Input,
Select, Select,
SelectItem SelectItem
} from '../../../components/v2'; } from '../../../components/v2';
import { import {
@@ -44,6 +44,7 @@ export default function GitLabCreateIntegrationPage() {
const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value); const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value);
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetAppId, setTargetAppId] = useState(''); const [targetAppId, setTargetAppId] = useState('');
const [targetEnvironment, setTargetEnvironment] = useState(''); const [targetEnvironment, setTargetEnvironment] = useState('');
@@ -101,7 +102,8 @@ export default function GitLabCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -136,6 +138,13 @@ export default function GitLabCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="GitLab Integration Type"> <FormControl label="GitLab Integration Type">
<Select <Select
value={targetEntity} value={targetEntity}
@@ -198,13 +207,11 @@ export default function GitLabCreateIntegrationPage() {
)} )}
</Select> </Select>
</FormControl> </FormControl>
<FormControl <FormControl label="GitLab Environment Scope (Optional)">
label="GitLab Environment Scope (Optional)" <Input
> placeholder="*"
<Input value={targetEnvironment}
placeholder="*" onChange={(e) => setTargetEnvironment(e.target.value)}
value={targetEnvironment}
onChange={(e) => setTargetEnvironment(e.target.value)}
/> />
</FormControl> </FormControl>
<Button <Button
@@ -1,8 +1,16 @@
import { useState } from 'react'; import { useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Input, Select, SelectItem } from '../../../components/v2';
import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth'; import { useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth';
import { useGetWorkspaceById } from '../../../hooks/api/workspace'; import { useGetWorkspaceById } from '../../../hooks/api/workspace';
import createIntegration from '../../api/integrations/createIntegration'; import createIntegration from '../../api/integrations/createIntegration';
@@ -16,60 +24,58 @@ export default function HashiCorpVaultCreateIntegrationPage() {
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? '');
const [vaultEnginePath, setVaultEnginePath] = useState(''); const [vaultEnginePath, setVaultEnginePath] = useState('');
const [vaultEnginePathErrorText, setVaultEnginePathErrorText ] = useState(''); const [vaultEnginePathErrorText, setVaultEnginePathErrorText] = useState('');
const [vaultSecretPath, setVaultSecretPath] = useState(''); const [vaultSecretPath, setVaultSecretPath] = useState('');
const [vaultSecretPathErrorText, setVaultSecretPathErrorText] = useState(''); const [vaultSecretPathErrorText, setVaultSecretPathErrorText] = useState('');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const isValidVaultPath = (secretPath: string) => { const isValidVaultPath = (vaultPath: string) => {
return !( return !(vaultPath.length === 0 || vaultPath.startsWith('/') || vaultPath.endsWith('/'));
secretPath.length === 0 ||
secretPath.startsWith('/') ||
secretPath.endsWith('/')
);
}; };
const handleButtonClick = async () => { const handleButtonClick = async () => {
try { try {
if (!integrationAuth?._id) return; if (!integrationAuth?._id) return;
if (!isValidVaultPath(vaultEnginePath)) { if (!isValidVaultPath(vaultEnginePath)) {
setVaultEnginePathErrorText('Vault KV Secrets Engine Path must be valid like kv'); setVaultEnginePathErrorText('Vault KV Secrets Engine Path must be valid like kv');
} else { } else {
setVaultEnginePathErrorText(''); setVaultEnginePathErrorText('');
} }
if (!isValidVaultPath(vaultSecretPath)) {
setVaultSecretPathErrorText('Vault Secret(s) Path must be valid like machine/dev');
} else {
setVaultSecretPathErrorText('');
}
if (!isValidVaultPath || !isValidVaultPath(vaultSecretPath)) return;
setIsLoading(true); if (!isValidVaultPath(vaultSecretPath)) {
setVaultSecretPathErrorText('Vault Secret(s) Path must be valid like machine/dev');
await createIntegration({ } else {
integrationAuthId: integrationAuth?._id, setVaultSecretPathErrorText('');
isActive: true, }
app: vaultEnginePath,
appId: null,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment: null,
targetEnvironmentId: null,
targetService: null,
targetServiceId: null,
owner: null,
path: vaultSecretPath,
region: null
});
setIsLoading(false);
router.push(`/integrations/${localStorage.getItem('projectData.id')}`); if (!isValidVaultPath || !isValidVaultPath(vaultSecretPath)) return;
setIsLoading(true);
await createIntegration({
integrationAuthId: integrationAuth?._id,
isActive: true,
app: vaultEnginePath,
appId: null,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment: null,
targetEnvironmentId: null,
targetService: null,
targetServiceId: null,
owner: null,
path: vaultSecretPath,
region: null,
secretPath
});
setIsLoading(false);
router.push(`/integrations/${localStorage.getItem('projectData.id')}`);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@@ -95,34 +101,41 @@ export default function HashiCorpVaultCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl <FormControl
label="Vault KV Secrets Engine Path" label="Vault KV Secrets Engine Path"
errorText={vaultEnginePathErrorText} errorText={vaultEnginePathErrorText}
isError={vaultEnginePathErrorText !== '' ?? false} isError={vaultEnginePathErrorText !== '' ?? false}
> >
<Input <Input
placeholder="kv" placeholder="kv"
value={vaultEnginePath} value={vaultEnginePath}
onChange={(e) => setVaultEnginePath(e.target.value)} onChange={(e) => setVaultEnginePath(e.target.value)}
/> />
</FormControl> </FormControl>
<FormControl <FormControl
label="Vault Secret(s) Path" label="Vault Secret(s) Path"
errorText={vaultSecretPathErrorText} errorText={vaultSecretPathErrorText}
isError={vaultSecretPathErrorText !== '' ?? false} isError={vaultSecretPathErrorText !== '' ?? false}
> >
<Input <Input
placeholder="machine/dev" placeholder="machine/dev"
value={vaultSecretPath} value={vaultSecretPath}
onChange={(e) => setVaultSecretPath(e.target.value)} onChange={(e) => setVaultSecretPath(e.target.value)}
/> />
</FormControl> </FormControl>
<Button <Button
onClick={handleButtonClick} onClick={handleButtonClick}
color="mineshaft" color="mineshaft"
className="mt-4" className="mt-4"
isLoading={isLoading} isLoading={isLoading}
isDisabled={!(isValidVaultPath(vaultEnginePath) && isValidVaultPath(vaultSecretPath))} isDisabled={!(isValidVaultPath(vaultEnginePath) && isValidVaultPath(vaultSecretPath))}
> >
Create Integration Create Integration
</Button> </Button>
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -23,6 +31,7 @@ export default function HerokuCreateIntegrationPage() {
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -60,7 +69,8 @@ export default function HerokuCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -94,6 +104,13 @@ export default function HerokuCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Heroku App" className="mt-4"> <FormControl label="Heroku App" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -31,7 +39,7 @@ export default function NetlifyCreateIntegrationPage() {
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [targetEnvironment, setTargetEnvironment] = useState(''); const [targetEnvironment, setTargetEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -71,7 +79,8 @@ export default function NetlifyCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -106,6 +115,13 @@ export default function NetlifyCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Netlify Site"> <FormControl label="Netlify Site">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById, useGetIntegrationAuthById,
@@ -20,6 +28,7 @@ export default function RailwayCreateIntegrationPage() {
const [targetServiceId, setTargetServiceId] = useState(''); const [targetServiceId, setTargetServiceId] = useState('');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { integrationAuthId } = queryString.parse(router.asPath.split('?')[1]); const { integrationAuthId } = queryString.parse(router.asPath.split('?')[1]);
@@ -99,7 +108,8 @@ export default function RailwayCreateIntegrationPage() {
targetServiceId: targetService ? targetService.serviceId : null, targetServiceId: targetService ? targetService.serviceId : null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -134,6 +144,13 @@ export default function RailwayCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Railway Project"> <FormControl label="Railway Project">
<Select <Select
value={targetAppId} value={targetAppId}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -23,7 +31,7 @@ export default function RenderCreateIntegrationPage() {
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -62,7 +70,8 @@ export default function RenderCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -97,6 +106,13 @@ export default function RenderCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Render Service" className="mt-4"> <FormControl label="Render Service" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -22,6 +30,7 @@ export default function SupabaseCreateIntegrationPage() {
}); });
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -62,7 +71,8 @@ export default function SupabaseCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -97,6 +107,13 @@ export default function SupabaseCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Supabase Project" className="mt-4"> <FormControl label="Supabase Project" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -23,7 +31,7 @@ export default function TravisCICreateIntegrationPage() {
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetApp, setTargetApp] = useState(''); const [targetApp, setTargetApp] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -62,7 +70,8 @@ export default function TravisCICreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path: null, path: null,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -97,6 +106,13 @@ export default function TravisCICreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Travis CI Project" className="mt-4"> <FormControl label="Travis CI Project" className="mt-4">
<Select <Select
value={targetApp} value={targetApp}
@@ -2,7 +2,15 @@ import { useEffect, useState } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import queryString from 'query-string'; import queryString from 'query-string';
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from '../../../components/v2'; import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from '../../../components/v2';
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById, useGetIntegrationAuthById,
@@ -21,6 +29,7 @@ export default function VercelCreateIntegrationPage() {
const router = useRouter(); const router = useRouter();
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [secretPath, setSecretPath] = useState('/');
const [targetAppId, setTargetAppId] = useState(''); const [targetAppId, setTargetAppId] = useState('');
const [targetEnvironment, setTargetEnvironment] = useState(''); const [targetEnvironment, setTargetEnvironment] = useState('');
const [targetBranch, setTargetBranch] = useState(''); const [targetBranch, setTargetBranch] = useState('');
@@ -84,7 +93,8 @@ export default function VercelCreateIntegrationPage() {
targetServiceId: null, targetServiceId: null,
owner: null, owner: null,
path, path,
region: null region: null,
secretPath
}); });
setIsLoading(false); setIsLoading(false);
@@ -119,6 +129,13 @@ export default function VercelCreateIntegrationPage() {
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Vercel App"> <FormControl label="Vercel App">
<Select <Select
value={targetAppId} value={targetAppId}