feat(#31): added multi env support in integration

This commit is contained in:
akhilmhdh
2023-01-13 21:09:59 +05:30
parent b67abf94d4
commit 036d32aeba
4 changed files with 139 additions and 83 deletions
@@ -34,12 +34,24 @@ interface IntegrationApp {
siteId: string; siteId: string;
} }
const Integration = ({ type Props = {
integration
}: {
integration: Integration; integration: Integration;
}) => { environments: Array<{ name: string; slug: string }>;
const [integrationEnvironment, setIntegrationEnvironment] = useState(integration.environment); };
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 [fileState, setFileState] = useState([]);
const router = useRouter(); const router = useRouter();
const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects
@@ -132,42 +144,47 @@ const Integration = ({
if (!integrationApp || apps.length === 0) return <div></div> if (!integrationApp || apps.length === 0) return <div></div>
return ( return (
<div className="max-w-5xl p-6 mx-6 mb-8 rounded-md bg-white/5 flex justify-between"> <div className='max-w-5xl p-6 mx-6 mb-8 rounded-md bg-white/5 flex justify-between'>
<div className="flex"> <div className='flex'>
<div> <div>
<p className="text-gray-400 text-xs font-semibold mb-2">ENVIRONMENT</p> <p className='text-gray-400 text-xs font-semibold mb-2'>
<ListBox data={!integration.isActive ? [ ENVIRONMENT
"Development", </p>
"Staging", <ListBox
"Testing", data={
"Production", !integration.isActive
] : null} ? environments.map(({ name }) => name)
selected={integrationEnvironment} : null
onChange={(environment) => { }
setIntegrationEnvironment(environment); selected={integrationEnvironment.name}
}} onChange={(envName) =>
setIntegrationEnvironment(
environments.find(({ name }) => envName === name) || {
name: 'unknown',
slug: 'unknown',
}
)
}
isFull={true} isFull={true}
/> />
</div> </div>
<div className="pt-2"> <div className='pt-2'>
<FontAwesomeIcon <FontAwesomeIcon
icon={faArrowRight} icon={faArrowRight}
className="mx-4 text-gray-400 mt-8" className='mx-4 text-gray-400 mt-8'
/> />
</div> </div>
<div className="mr-2"> <div className='mr-2'>
<p className="text-gray-400 text-xs font-semibold mb-2"> <p className='text-gray-400 text-xs font-semibold mb-2'>
INTEGRATION INTEGRATION
</p> </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.charAt(0).toUpperCase() +
integration.integration.slice(1)} integration.integration.slice(1)}
</div> </div>
</div> </div>
<div className="mr-2"> <div className='mr-2'>
<div className="text-gray-400 text-xs font-semibold mb-2"> <div className='text-gray-400 text-xs font-semibold mb-2'>APP</div>
APP
</div>
<ListBox <ListBox
data={!integration.isActive ? apps.map((app) => app.name) : null} data={!integration.isActive ? apps.map((app) => app.name) : null}
selected={integrationApp} selected={integrationApp}
@@ -178,52 +195,55 @@ const Integration = ({
</div> </div>
{renderIntegrationSpecificParams(integration)} {renderIntegrationSpecificParams(integration)}
</div> </div>
<div className="flex items-end"> <div className='flex items-end'>
{integration.isActive ? ( {integration.isActive ? (
<div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4"> <div className='max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4'>
<FontAwesomeIcon <FontAwesomeIcon
icon={faRotate} icon={faRotate}
className="text-lg mr-2.5 text-primary animate-spin" 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='text-gray-300 font-semibold'>In Sync</div>
</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>
</div> </div>
); );
@@ -5,7 +5,8 @@ import guidGenerator from "~/utilities/randomId";
import Integration from "./Integration"; import Integration from "./Integration";
interface Props { interface Props {
integrations: any integrations: any;
environments: Array<{ name: string; slug: string }>;
} }
interface IntegrationType { interface IntegrationType {
@@ -19,7 +20,8 @@ interface IntegrationType {
} }
const ProjectIntegrationSection = ({ const ProjectIntegrationSection = ({
integrations integrations,
environments = [],
}: Props) => { }: Props) => {
return integrations.length > 0 ? ( return integrations.length > 0 ? (
<div className="mb-12"> <div className="mb-12">
@@ -33,6 +35,7 @@ const ProjectIntegrationSection = ({
<Integration <Integration
key={guidGenerator()} key={guidGenerator()}
integration={integration} integration={integration}
environments={environments}
/> />
))} ))}
</div> </div>
@@ -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;
+11 -9
View File
@@ -24,6 +24,7 @@ import setBotActiveStatus from "../api/bot/setBotActiveStatus";
import getIntegrationOptions from "../api/integrations/GetIntegrationOptions"; import getIntegrationOptions from "../api/integrations/GetIntegrationOptions";
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations"; import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations"; import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import getAWorkspace from "../api/workspace/getAWorkspace";
import getLatestFileKey from "../api/workspace/getLatestFileKey"; import getLatestFileKey from "../api/workspace/getLatestFileKey";
const { const {
decryptAssymmetric, decryptAssymmetric,
@@ -34,6 +35,7 @@ const crypto = require("crypto");
export default function Integrations() { export default function Integrations() {
const [cloudIntegrationOptions, setCloudIntegrationOptions] = useState([]); const [cloudIntegrationOptions, setCloudIntegrationOptions] = useState([]);
const [integrationAuths, setIntegrationAuths] = useState([]); const [integrationAuths, setIntegrationAuths] = useState([]);
const [environments,setEnvironments] = useState([])
const [integrations, setIntegrations] = useState([]); const [integrations, setIntegrations] = useState([]);
const [bot, setBot] = useState(null); const [bot, setBot] = useState(null);
const [isActivateBotDialogOpen, setIsActivateBotDialogOpen] = useState(false); const [isActivateBotDialogOpen, setIsActivateBotDialogOpen] = useState(false);
@@ -41,11 +43,15 @@ export default function Integrations() {
const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null); const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null);
const router = useRouter(); const router = useRouter();
const workspaceId = router.query.id;
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(async () => { useEffect(async () => {
try { try {
const workspace = await getAWorkspace(workspaceId);
setEnvironments(workspace.environments);
// get cloud integration options // get cloud integration options
setCloudIntegrationOptions( setCloudIntegrationOptions(
await getIntegrationOptions() await getIntegrationOptions()
@@ -54,23 +60,19 @@ export default function Integrations() {
// get project integration authorizations // get project integration authorizations
setIntegrationAuths( setIntegrationAuths(
await getWorkspaceAuthorizations({ await getWorkspaceAuthorizations({
workspaceId: router.query.id, workspaceId
}) })
); );
// get project integrations // get project integrations
setIntegrations( setIntegrations(
await getWorkspaceIntegrations({ await getWorkspaceIntegrations({
workspaceId: router.query.id, workspaceId,
}) })
); );
// get project bot // get project bot
setBot( setBot(await getBot({ workspaceId }));
await getBot({
workspaceId: router.query.id
}
));
} catch (err) { } catch (err) {
console.log(err); console.log(err);
@@ -90,7 +92,7 @@ export default function Integrations() {
if (bot) { if (bot) {
// case: there is a 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 PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
const WORKSPACE_KEY = decryptAssymmetric({ const WORKSPACE_KEY = decryptAssymmetric({
@@ -214,7 +216,7 @@ export default function Integrations() {
handleBotActivate={handleBotActivate} handleBotActivate={handleBotActivate}
handleIntegrationOption={handleIntegrationOption} handleIntegrationOption={handleIntegrationOption}
/> */} /> */}
<IntegrationSection integrations={integrations} /> <IntegrationSection integrations={integrations} environments={environments} />
{(cloudIntegrationOptions.length > 0 && bot) ? ( {(cloudIntegrationOptions.length > 0 && bot) ? (
<CloudIntegrationSection <CloudIntegrationSection
cloudIntegrationOptions={cloudIntegrationOptions} cloudIntegrationOptions={cloudIntegrationOptions}