mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(#31): added multi env support in integration
This commit is contained in:
@@ -34,12 +34,24 @@ interface IntegrationApp {
|
||||
siteId: string;
|
||||
}
|
||||
|
||||
const Integration = ({
|
||||
integration
|
||||
}: {
|
||||
type Props = {
|
||||
integration: Integration;
|
||||
}) => {
|
||||
const [integrationEnvironment, setIntegrationEnvironment] = useState(integration.environment);
|
||||
environments: Array<{ name: string; slug: string }>;
|
||||
};
|
||||
|
||||
const Integration = ({
|
||||
integration,
|
||||
environments = []
|
||||
}:Props ) => {
|
||||
// set initial environment. This find will only execute when component is mounting
|
||||
const [integrationEnvironment, setIntegrationEnvironment] = useState<
|
||||
Props['environments'][0]
|
||||
>(
|
||||
environments.find(({ slug }) => slug === integration.environment) || {
|
||||
name: '',
|
||||
slug: '',
|
||||
}
|
||||
);
|
||||
const [fileState, setFileState] = useState([]);
|
||||
const router = useRouter();
|
||||
const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects
|
||||
@@ -132,42 +144,47 @@ const Integration = ({
|
||||
if (!integrationApp || apps.length === 0) return <div></div>
|
||||
|
||||
return (
|
||||
<div className="max-w-5xl p-6 mx-6 mb-8 rounded-md bg-white/5 flex justify-between">
|
||||
<div className="flex">
|
||||
<div className='max-w-5xl p-6 mx-6 mb-8 rounded-md bg-white/5 flex justify-between'>
|
||||
<div className='flex'>
|
||||
<div>
|
||||
<p className="text-gray-400 text-xs font-semibold mb-2">ENVIRONMENT</p>
|
||||
<ListBox data={!integration.isActive ? [
|
||||
"Development",
|
||||
"Staging",
|
||||
"Testing",
|
||||
"Production",
|
||||
] : null}
|
||||
selected={integrationEnvironment}
|
||||
onChange={(environment) => {
|
||||
setIntegrationEnvironment(environment);
|
||||
}}
|
||||
<p className='text-gray-400 text-xs font-semibold mb-2'>
|
||||
ENVIRONMENT
|
||||
</p>
|
||||
<ListBox
|
||||
data={
|
||||
!integration.isActive
|
||||
? environments.map(({ name }) => name)
|
||||
: null
|
||||
}
|
||||
selected={integrationEnvironment.name}
|
||||
onChange={(envName) =>
|
||||
setIntegrationEnvironment(
|
||||
environments.find(({ name }) => envName === name) || {
|
||||
name: 'unknown',
|
||||
slug: 'unknown',
|
||||
}
|
||||
)
|
||||
}
|
||||
isFull={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<div className='pt-2'>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="mx-4 text-gray-400 mt-8"
|
||||
/>
|
||||
className='mx-4 text-gray-400 mt-8'
|
||||
/>
|
||||
</div>
|
||||
<div className="mr-2">
|
||||
<p className="text-gray-400 text-xs font-semibold mb-2">
|
||||
<div className='mr-2'>
|
||||
<p className='text-gray-400 text-xs font-semibold mb-2'>
|
||||
INTEGRATION
|
||||
</p>
|
||||
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-10 text-sm font-semibold text-gray-300">
|
||||
<div className='py-2.5 bg-white/[.07] rounded-md pl-4 pr-10 text-sm font-semibold text-gray-300'>
|
||||
{integration.integration.charAt(0).toUpperCase() +
|
||||
integration.integration.slice(1)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mr-2">
|
||||
<div className="text-gray-400 text-xs font-semibold mb-2">
|
||||
APP
|
||||
</div>
|
||||
<div className='mr-2'>
|
||||
<div className='text-gray-400 text-xs font-semibold mb-2'>APP</div>
|
||||
<ListBox
|
||||
data={!integration.isActive ? apps.map((app) => app.name) : null}
|
||||
selected={integrationApp}
|
||||
@@ -178,52 +195,55 @@ const Integration = ({
|
||||
</div>
|
||||
{renderIntegrationSpecificParams(integration)}
|
||||
</div>
|
||||
<div className="flex items-end">
|
||||
{integration.isActive ? (
|
||||
<div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4">
|
||||
<FontAwesomeIcon
|
||||
icon={faRotate}
|
||||
className="text-lg mr-2.5 text-primary animate-spin"
|
||||
/>
|
||||
<div className="text-gray-300 font-semibold">In Sync</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
text="Start Integration"
|
||||
onButtonPressed={async () => {
|
||||
|
||||
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
|
||||
const siteId = siteApp?.siteId ? siteApp.siteId : null;
|
||||
|
||||
await updateIntegration({
|
||||
integrationId: integration._id,
|
||||
environment: integrationEnvironment,
|
||||
app: integrationApp,
|
||||
isActive: true,
|
||||
target: integrationTarget ? integrationTarget.toLowerCase() : null,
|
||||
context: integrationContext ? reverseContextNetlifyMapping[integrationContext] : null,
|
||||
siteId
|
||||
});
|
||||
|
||||
router.reload();
|
||||
}}
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 ml-2">
|
||||
<Button
|
||||
onButtonPressed={async () => {
|
||||
await deleteIntegration({
|
||||
integrationId: integration._id,
|
||||
});
|
||||
router.reload();
|
||||
}}
|
||||
color="red"
|
||||
size="icon-md"
|
||||
icon={faX}
|
||||
<div className='flex items-end'>
|
||||
{integration.isActive ? (
|
||||
<div className='max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4'>
|
||||
<FontAwesomeIcon
|
||||
icon={faRotate}
|
||||
className='text-lg mr-2.5 text-primary animate-spin'
|
||||
/>
|
||||
<div className='text-gray-300 font-semibold'>In Sync</div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
text='Start Integration'
|
||||
onButtonPressed={async () => {
|
||||
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
|
||||
const siteId = siteApp?.siteId ? siteApp.siteId : null;
|
||||
|
||||
await updateIntegration({
|
||||
integrationId: integration._id,
|
||||
environment: integrationEnvironment.slug,
|
||||
app: integrationApp,
|
||||
isActive: true,
|
||||
target: integrationTarget
|
||||
? integrationTarget.toLowerCase()
|
||||
: null,
|
||||
context: integrationContext
|
||||
? reverseContextNetlifyMapping[integrationContext]
|
||||
: null,
|
||||
siteId,
|
||||
});
|
||||
|
||||
router.reload();
|
||||
}}
|
||||
color='mineshaft'
|
||||
size='md'
|
||||
/>
|
||||
)}
|
||||
<div className='opacity-50 hover:opacity-100 duration-200 ml-2'>
|
||||
<Button
|
||||
onButtonPressed={async () => {
|
||||
await deleteIntegration({
|
||||
integrationId: integration._id,
|
||||
});
|
||||
router.reload();
|
||||
}}
|
||||
color='red'
|
||||
size='icon-md'
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,8 @@ import guidGenerator from "~/utilities/randomId";
|
||||
import Integration from "./Integration";
|
||||
|
||||
interface Props {
|
||||
integrations: any
|
||||
integrations: any;
|
||||
environments: Array<{ name: string; slug: string }>;
|
||||
}
|
||||
|
||||
interface IntegrationType {
|
||||
@@ -19,7 +20,8 @@ interface IntegrationType {
|
||||
}
|
||||
|
||||
const ProjectIntegrationSection = ({
|
||||
integrations
|
||||
integrations,
|
||||
environments = [],
|
||||
}: Props) => {
|
||||
return integrations.length > 0 ? (
|
||||
<div className="mb-12">
|
||||
@@ -33,6 +35,7 @@ const ProjectIntegrationSection = ({
|
||||
<Integration
|
||||
key={guidGenerator()}
|
||||
integration={integration}
|
||||
environments={environments}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
31
frontend/pages/api/workspace/getAWorkspace.ts
Normal file
31
frontend/pages/api/workspace/getAWorkspace.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import SecurityClient from '~/utilities/SecurityClient';
|
||||
|
||||
interface Workspace {
|
||||
__v: number;
|
||||
_id: string;
|
||||
name: string;
|
||||
organization: string;
|
||||
environments: Array<{ name: string; slug: string }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This route lets us get the workspaces of a certain user
|
||||
* @returns
|
||||
*/
|
||||
const getAWorkspace = (workspaceID:string) => {
|
||||
return SecurityClient.fetchCall(`/api/v1/workspace/${workspaceID}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(async (res) => {
|
||||
if (res?.status == 200) {
|
||||
const data = (await res.json()) as unknown as { workspace: Workspace };
|
||||
return data.workspace;
|
||||
}
|
||||
|
||||
throw new Error('Failed to get workspace');
|
||||
});
|
||||
};
|
||||
|
||||
export default getAWorkspace;
|
||||
@@ -24,6 +24,7 @@ import setBotActiveStatus from "../api/bot/setBotActiveStatus";
|
||||
import getIntegrationOptions from "../api/integrations/GetIntegrationOptions";
|
||||
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
|
||||
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
|
||||
import getAWorkspace from "../api/workspace/getAWorkspace";
|
||||
import getLatestFileKey from "../api/workspace/getLatestFileKey";
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
@@ -34,6 +35,7 @@ const crypto = require("crypto");
|
||||
export default function Integrations() {
|
||||
const [cloudIntegrationOptions, setCloudIntegrationOptions] = useState([]);
|
||||
const [integrationAuths, setIntegrationAuths] = useState([]);
|
||||
const [environments,setEnvironments] = useState([])
|
||||
const [integrations, setIntegrations] = useState([]);
|
||||
const [bot, setBot] = useState(null);
|
||||
const [isActivateBotDialogOpen, setIsActivateBotDialogOpen] = useState(false);
|
||||
@@ -41,11 +43,15 @@ export default function Integrations() {
|
||||
const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null);
|
||||
|
||||
const router = useRouter();
|
||||
const workspaceId = router.query.id;
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(async () => {
|
||||
try {
|
||||
const workspace = await getAWorkspace(workspaceId);
|
||||
setEnvironments(workspace.environments);
|
||||
|
||||
// get cloud integration options
|
||||
setCloudIntegrationOptions(
|
||||
await getIntegrationOptions()
|
||||
@@ -54,23 +60,19 @@ export default function Integrations() {
|
||||
// get project integration authorizations
|
||||
setIntegrationAuths(
|
||||
await getWorkspaceAuthorizations({
|
||||
workspaceId: router.query.id,
|
||||
workspaceId
|
||||
})
|
||||
);
|
||||
|
||||
// get project integrations
|
||||
setIntegrations(
|
||||
await getWorkspaceIntegrations({
|
||||
workspaceId: router.query.id,
|
||||
workspaceId,
|
||||
})
|
||||
);
|
||||
|
||||
// get project bot
|
||||
setBot(
|
||||
await getBot({
|
||||
workspaceId: router.query.id
|
||||
}
|
||||
));
|
||||
setBot(await getBot({ workspaceId }));
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@@ -90,7 +92,7 @@ export default function Integrations() {
|
||||
|
||||
if (bot) {
|
||||
// case: there is a bot
|
||||
const key = await getLatestFileKey({ workspaceId: router.query.id });
|
||||
const key = await getLatestFileKey({ workspaceId });
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||
|
||||
const WORKSPACE_KEY = decryptAssymmetric({
|
||||
@@ -214,7 +216,7 @@ export default function Integrations() {
|
||||
handleBotActivate={handleBotActivate}
|
||||
handleIntegrationOption={handleIntegrationOption}
|
||||
/> */}
|
||||
<IntegrationSection integrations={integrations} />
|
||||
<IntegrationSection integrations={integrations} environments={environments} />
|
||||
{(cloudIntegrationOptions.length > 0 && bot) ? (
|
||||
<CloudIntegrationSection
|
||||
cloudIntegrationOptions={cloudIntegrationOptions}
|
||||
|
||||
Reference in New Issue
Block a user