Fix Typescript issues for frontend integrations

This commit is contained in:
Tuan Dang
2022-12-18 18:57:50 -05:00
parent 6ea26c135a
commit 91052df5f9
15 changed files with 160 additions and 128 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ services:
- 80:80 - 80:80
- 443:443 - 443:443
volumes: volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
depends_on: depends_on:
- frontend - frontend
- backend - backend
+3 -3
View File
@@ -10,10 +10,10 @@ import { Listbox, Transition } from "@headlessui/react";
interface ListBoxProps { interface ListBoxProps {
selected: string; selected: string;
onChange: () => void; onChange: (arg: string) => void;
data: string[]; data: string[];
text: string; text?: string;
buttonAction: () => void; buttonAction?: () => void;
isFull?: boolean; isFull?: boolean;
} }
+1 -1
View File
@@ -9,7 +9,7 @@ import {
const classNames = require("classnames"); const classNames = require("classnames");
type ButtonProps = { type ButtonProps = {
text: string; text?: string;
onButtonPressed: () => void; onButtonPressed: () => void;
loading?: boolean; loading?: boolean;
color?: string; color?: string;
@@ -1,30 +1,32 @@
import React from "react"; import React from "react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faCheck, faCheck,
faX, faX,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import deleteIntegrationAuth from "../../pages/api/integrations/DeleteIntegrationAuth"; import deleteIntegrationAuth from "../../pages/api/integrations/DeleteIntegrationAuth";
interface CloudIntegrationOption { interface CloudIntegrationOption {
isAvailable: Boolean; isAvailable: boolean;
name: string; name: string;
type: string; type: string;
clientId: string; clientId: string;
docsLink: string; docsLink: string;
slug: string;
} }
interface IntegrationAuth { interface IntegrationAuth {
id: string; _id: string;
integration: string; integration: string;
} }
interface Props { interface Props {
cloudIntegrationOption: CloudIntegrationOption; cloudIntegrationOption: CloudIntegrationOption;
setSelectedIntegrationOption: () => void; setSelectedIntegrationOption: (cloudIntegration: CloudIntegrationOption) => void;
integrationOptionPress: () => void; integrationOptionPress: (cloudIntegrationOption: CloudIntegrationOption) => void;
integrationAuths: IntegrationAuth[]; integrationAuths: IntegrationAuth[];
} }
@@ -45,9 +47,7 @@ const CloudIntegration = ({
onClick={() => { onClick={() => {
if (!cloudIntegrationOption.isAvailable) return; if (!cloudIntegrationOption.isAvailable) return;
setSelectedIntegrationOption(cloudIntegrationOption); setSelectedIntegrationOption(cloudIntegrationOption);
integrationOptionPress({ integrationOptionPress(cloudIntegrationOption);
integrationOption: cloudIntegrationOption
});
}} }}
key={cloudIntegrationOption.name} key={cloudIntegrationOption.name}
> >
@@ -1,11 +1,14 @@
import React from "react"; import React from "react";
import CloudIntegration from "./CloudIntegration"; import CloudIntegration from "./CloudIntegration";
interface CloudIntegrationOption { interface CloudIntegrationOption {
isAvailable: boolean;
name: string; name: string;
type: string; type: string;
clientId: string; clientId: string;
docsLink: string; docsLink: string;
slug: string;
} }
interface Props { interface Props {
@@ -1,14 +1,17 @@
import React from "react"; import React from "react";
import FrameworkIntegration from "./FrameworkIntegration"; import FrameworkIntegration from "./FrameworkIntegration";
interface Framework { interface Framework {
name: string; name: string;
image: string; image: string;
link: string; link: string;
slug: string;
docsLink: string;
} }
interface Props { interface Props {
framework: Framework frameworks: [Framework]
} }
const FrameworkIntegrationSection = ({ frameworks }: Props) => { const FrameworkIntegrationSection = ({ frameworks }: Props) => {
@@ -6,23 +6,30 @@ import {
faX, faX,
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
envMapping,
reverseEnvMapping,
reverseContextNetlifyMapping
} from "../../public/data/frequentConstants";
import updateIntegration from "../../pages/api/integrations/updateIntegration"
import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
import Button from "~/components/basic/buttons/Button"; import Button from "~/components/basic/buttons/Button";
import ListBox from "~/components/basic/Listbox"; import ListBox from "~/components/basic/Listbox";
import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
import updateIntegration from "../../pages/api/integrations/updateIntegration"
import {
envMapping,
reverseContextNetlifyMapping,
reverseEnvMapping} from "../../public/data/frequentConstants";
interface Integration { interface Integration {
_id: string;
app?: string; app?: string;
environment: string; environment: string;
integration: string; integration: string;
integrationAuth: string; integrationAuth: string;
isActive: Boolean; isActive: boolean;
}
interface IntegrationApp {
name: string;
siteId: string;
} }
const Integration = ({ const Integration = ({
@@ -35,39 +42,44 @@ const Integration = ({
); );
const [fileState, setFileState] = useState([]); const [fileState, setFileState] = useState([]);
const router = useRouter(); const router = useRouter();
const [apps, setApps] = useState([]); // integration app objects const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects
const [integrationApp, setIntegrationApp] = useState(null); // integration app name const [integrationApp, setIntegrationApp] = useState(""); // integration app name
const [integrationTarget, setIntegrationTarget] = useState(null); // vercel-specific integration param const [integrationTarget, setIntegrationTarget] = useState(""); // vercel-specific integration param
const [integrationContext, setIntegrationContext] = useState(null); // netlify-specific integration param const [integrationContext, setIntegrationContext] = useState(""); // netlify-specific integration param
useEffect(async () => { useEffect(() => {
interface App {
name: string;
siteId?: string;
}
const tempApps = await getIntegrationApps({ const loadIntegration = async () => {
integrationAuthId: integration.integrationAuth, interface App {
}); name: string;
siteId?: string;
setApps(tempApps); }
setIntegrationApp(
integration.app ? integration.app : tempApps[0].name const tempApps: [IntegrationApp] = await getIntegrationApps({
); integrationAuthId: integration.integrationAuth,
});
switch (integration.integration) {
case "vercel": setApps(tempApps);
setIntegrationTarget("Development"); setIntegrationApp(
break; integration.app ? integration.app : tempApps[0].name
case "netlify": );
setIntegrationContext("All");
break; switch (integration.integration) {
default: case "vercel":
break; setIntegrationTarget("Development");
break;
case "netlify":
setIntegrationContext("All");
break;
default:
break;
}
} }
loadIntegration();
}, []); }, []);
const renderIntegrationSpecificParams = (integration) => { const renderIntegrationSpecificParams = (integration: Integration) => {
try { try {
switch (integration.integration) { switch (integration.integration) {
case "vercel": case "vercel":
@@ -77,11 +89,11 @@ const Integration = ({
ENVIRONMENT ENVIRONMENT
</div> </div>
<ListBox <ListBox
data={!integration.isActive && [ data={!integration.isActive ? [
"Production", "Production",
"Preview", "Preview",
"Development" "Development"
]} ] : []}
selected={"Production"} selected={"Production"}
onChange={setIntegrationTarget} onChange={setIntegrationTarget}
/> />
@@ -94,13 +106,13 @@ const Integration = ({
CONTEXT CONTEXT
</div> </div>
<ListBox <ListBox
data={!integration.isActive && [ data={!integration.isActive ? [
"All", "All",
"Production", "Production",
"Deploy previews", "Deploy previews",
"Branch deploys", "Branch deploys",
"Local development" "Local development"
]} ] : []}
selected={integrationContext} selected={integrationContext}
onChange={setIntegrationContext} onChange={setIntegrationContext}
/> />
@@ -126,9 +138,11 @@ const Integration = ({
"Staging", "Staging",
"Testing", "Testing",
"Production", "Production",
] : null} ] : []}
selected={integrationEnvironment} selected={integrationEnvironment}
onChange={setIntegrationEnvironment} onChange={(environment) => {
setIntegrationEnvironment(environment);
}}
isFull={true} isFull={true}
/> />
</div> </div>
@@ -152,9 +166,11 @@ const Integration = ({
APP APP
</div> </div>
<ListBox <ListBox
data={!integration.isActive && apps.map((app) => app.name)} data={!integration.isActive ? apps.map((app) => app.name) : []}
selected={integrationApp} selected={integrationApp}
onChange={setIntegrationApp} onChange={(app) => {
setIntegrationApp(app);
}}
/> />
</div> </div>
{renderIntegrationSpecificParams(integration)} {renderIntegrationSpecificParams(integration)}
@@ -172,7 +188,9 @@ const Integration = ({
<Button <Button
text="Start Integration" text="Start Integration"
onButtonPressed={async () => { onButtonPressed={async () => {
const siteId = apps.find((app) => app.name === integrationApp).siteId ? apps.find((app) => app.name === integrationApp).siteId : null;
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
const siteId = siteApp ? siteApp.siteId : null;
const result = await updateIntegration({ const result = await updateIntegration({
integrationId: integration._id, integrationId: integration._id,
environment: envMapping[integrationEnvironment], environment: envMapping[integrationEnvironment],
@@ -1,15 +1,22 @@
import React from "react"; import React from "react";
import Integration from "./Integration";
import guidGenerator from "~/utilities/randomId"; import guidGenerator from "~/utilities/randomId";
interface Integration { import Integration from "./Integration";
}
interface Props { interface Props {
integrations: any integrations: any
} }
interface IntegrationType {
_id: string;
app?: string;
environment: string;
integration: string;
integrationAuth: string;
isActive: boolean;
}
const ProjectIntegrationSection = ({ const ProjectIntegrationSection = ({
integrations integrations
}: Props) => { }: Props) => {
@@ -21,12 +28,12 @@ const ProjectIntegrationSection = ({
Manage your integrations of Infisical with third-party services. Manage your integrations of Infisical with third-party services.
</p> </p>
</div> </div>
{integrations.map((integration => ( {integrations.map((integration: IntegrationType) => (
<Integration <Integration
key={guidGenerator()} key={guidGenerator()}
integration={integration} integration={integration}
/> />
)))} ))}
</div> </div>
) : <div></div> ) : <div></div>
} }
@@ -4,15 +4,11 @@ const POSTHOG_HOST =
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com"; process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!; const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!; const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
const CLIENT_ID_HEROKU = process.env.NEXT_PUBLIC_CLIENT_ID_HEROKU!;
const CLIENT_ID_NETLIFY = process.env.NEXT_PUBLIC_CLIENT_ID_NETLIFY!;
export { export {
ENV, ENV,
POSTHOG_API_KEY, POSTHOG_API_KEY,
POSTHOG_HOST, POSTHOG_HOST,
STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_STARTER, STRIPE_PRODUCT_STARTER
CLIENT_ID_HEROKU,
CLIENT_ID_NETLIFY
}; };
+1 -1
View File
@@ -7,7 +7,7 @@ interface BotKey {
interface Props { interface Props {
botId: string; botId: string;
isActive: Boolean; isActive: boolean;
botKey: BotKey; botKey: BotKey;
} }
@@ -22,6 +22,15 @@ const updateIntegration = ({
target, target,
context, context,
siteId siteId
}: {
integrationId: string,
app: string,
environment: string,
isActive: boolean,
target: string | null,
context: string | null,
siteId: string | null
}) => { }) => {
return SecurityClient.fetchCall( return SecurityClient.fetchCall(
"/api/v1/integration/" + integrationId, "/api/v1/integration/" + integrationId,
@@ -40,7 +49,7 @@ const updateIntegration = ({
}), }),
} }
).then(async (res) => { ).then(async (res) => {
if (res.status == 200) { if (res && res.status == 200) {
return res; return res;
} else { } else {
console.log("Failed to start an integration"); console.log("Failed to start an integration");
+19 -18
View File
@@ -2,20 +2,22 @@ import React, { useEffect, useState } from "react";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import NavHeader from "~/components/navigation/NavHeader";
import Integration from "~/components/integrations/Integration";
import FrameworkIntegrationSection from "~/components/integrations/FrameworkIntegrationSection";
import CloudIntegrationSection from "~/components/integrations/CloudIntegrationSection";
import IntegrationSection from "~/components/integrations/IntegrationSection";
import frameworkIntegrationOptions from "../../public/json/frameworkIntegrations.json";
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import getIntegrationOptions from "../api/integrations/GetIntegrationOptions";
import getBot from "../api/bot/getBot";
import setBotActiveStatus from "../api/bot/setBotActiveStatus";
import getLatestFileKey from "../api/workspace/getLatestFileKey";
import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog"; import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
import IntegrationAccessTokenDialog from "~/components/basic/dialog/IntegrationAccessTokenDialog"; import IntegrationAccessTokenDialog from "~/components/basic/dialog/IntegrationAccessTokenDialog";
import CloudIntegrationSection from "~/components/integrations/CloudIntegrationSection";
import FrameworkIntegrationSection from "~/components/integrations/FrameworkIntegrationSection";
import Integration from "~/components/integrations/Integration";
import IntegrationSection from "~/components/integrations/IntegrationSection";
import NavHeader from "~/components/navigation/NavHeader";
import frameworkIntegrationOptions from "../../public/json/frameworkIntegrations.json";
import getBot from "../api/bot/getBot";
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 getLatestFileKey from "../api/workspace/getLatestFileKey";
const { const {
decryptAssymmetric, decryptAssymmetric,
encryptAssymmetric encryptAssymmetric
@@ -150,14 +152,13 @@ export default function Integrations() {
/** /**
* Open dialog to activate bot if bot is not active. * Open dialog to activate bot if bot is not active.
* Otherwise, start integration [integrationOption] * Otherwise, start integration [integrationOption]
* @param {Object} obj * @param {Object} integrationOption - an integration option
* @param {Object} obj.integrationOption - an integration option * @param {String} integrationOption.name
* @param {String} obj.name * @param {String} integrationOption.type
* @param {String} obj.type * @param {String} integrationOption.docsLink
* @param {String} obj.docsLink
* @returns * @returns
*/ */
const integrationOptionPress = ({ integrationOption }) => { const integrationOptionPress = (integrationOption) => {
try { try {
if (bot.isActive) { if (bot.isActive) {
// case: bot is active -> proceed with integration // case: bot is active -> proceed with integration
+11 -13
View File
@@ -16,20 +16,18 @@ export default function Netlify() {
*/ */
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => { useEffect(async () => {
if (state === localStorage.getItem('latestCSRFToken')) {
localStorage.removeItem('latestCSRFToken');
await AuthorizeIntegration({
workspaceId: localStorage.getItem('projectData.id'),
code,
integration: "netlify"
});
router.push("/integrations/" + localStorage.getItem("projectData.id"));
}
try { try {
if (state === localStorage.getItem('latestCSRFToken')) {
localStorage.removeItem('latestCSRFToken');
await AuthorizeIntegration({
workspaceId: localStorage.getItem('projectData.id'),
code,
integration: "netlify"
});
router.push("/integrations/" + localStorage.getItem("projectData.id"));
}
} catch (err) { } catch (err) {
console.error('Netlify integration error: ', err); console.error('Netlify integration error: ', err);
} }
+11 -13
View File
@@ -15,20 +15,18 @@ export default function Vercel() {
*/ */
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => { useEffect(async () => {
if (state === localStorage.getItem('latestCSRFToken')) {
localStorage.removeItem('latestCSRFToken');
await AuthorizeIntegration({
workspaceId: localStorage.getItem('projectData.id'),
code,
integration: "vercel"
});
router.push("/integrations/" + localStorage.getItem("projectData.id"));
}
try { try {
if (state === localStorage.getItem('latestCSRFToken')) {
localStorage.removeItem('latestCSRFToken');
await AuthorizeIntegration({
workspaceId: localStorage.getItem('projectData.id'),
code,
integration: "vercel"
});
router.push("/integrations/" + localStorage.getItem("projectData.id"));
}
} catch (err) { } catch (err) {
console.error('Vercel integration error: ', err); console.error('Vercel integration error: ', err);
} }
+10 -11
View File
@@ -1,22 +1,22 @@
const envMapping = { interface Mapping {
[key: string]: string;
}
const envMapping: Mapping = {
Development: "dev", Development: "dev",
Staging: "staging", Staging: "staging",
Production: "prod", Production: "prod",
Testing: "test", Testing: "test",
}; };
const reverseEnvMapping = { const reverseEnvMapping: Mapping = {
dev: "Development", dev: "Development",
staging: "Staging", staging: "Staging",
prod: "Production", prod: "Production",
test: "Testing", test: "Testing",
}; };
const vercelMapping = { const reverseContextNetlifyMapping: Mapping = {
}
const reverseContextNetlifyMapping = {
"All": "all", "All": "all",
"Local development": "dev", "Local development": "dev",
"Branch deploys": "branch-deploy", "Branch deploys": "branch-deploy",
@@ -26,6 +26,5 @@ const reverseContextNetlifyMapping = {
export { export {
envMapping, envMapping,
reverseEnvMapping, reverseContextNetlifyMapping,
reverseContextNetlifyMapping reverseEnvMapping};
};