Skip to content

Commit

Permalink
feat: [SIG-526]: UI Integrations V0 (#4595)
Browse files Browse the repository at this point in the history
* feat: integrations v0 base setup routes and components

* chore: typecheck fix

* feat: integrations landing page changes

* feat: initial header setup

* feat: integrations list page setup

* feat: integrations details content root setup

* feat: integration detail content setup

* feat: added overview tab

* feat: added data tab

* feat: handle configuration tab

* feat: add min height for the container

* feat: generate apis and hooks for usage

* feat: added remove integration modal

* feat: added remove integration modal

* feat: added remove integration modal

* feat: added test connection bars

* chore: add bottom margins

* feat: added test connection modal

* feat: add all types of test connection

* feat: add all types of test connection

* fix: address review comments

* fix: address review comments

* feat: added get all integrations API and search bar implemnetation

* feat: navigate to overview section in case of row click and configure in btn

* feat: integrate get integration details api

* feat: handle integration details page gracefully

* feat: integrate uninstall API and the connection states

* feat: add install integration API call

* feat: added api error handling

* feat: handle error states for list and details api

* feat: handle the logs and metrics columns

* feat: add TODOs for pending tasks

* feat: comment from side nav

* feat: added support for custom tags in react markdown

* chore: revert the temporary change for merge

* feat: integrate the status api calls and polling logic

* chore: add markdown components and correct the polling issue

* chore: handle light mode

* chore: remove integrations from sideNav

* fix: address review comments

* fix: address review comments
  • Loading branch information
vikrantgupta25 authored Mar 6, 2024
1 parent 7136ecc commit 0c41492
Show file tree
Hide file tree
Showing 43 changed files with 2,814 additions and 6 deletions.
5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"react-virtuoso": "4.0.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"rehype-raw": "7.0.0",
"stream": "^0.0.2",
"style-loader": "1.3.0",
"styled-components": "^5.3.11",
Expand Down Expand Up @@ -203,6 +204,7 @@
"jest-styled-components": "^7.0.8",
"lint-staged": "^12.5.0",
"msw": "1.3.2",
"npm-run-all": "latest",
"portfinder-sync": "^0.0.2",
"prettier": "2.2.1",
"raw-loader": "4.0.2",
Expand All @@ -216,8 +218,7 @@
"ts-node": "^10.2.1",
"typescript-plugin-css-modules": "5.0.1",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"npm-run-all": "latest"
"webpack-cli": "^4.9.2"
},
"lint-staged": {
"*.(js|jsx|ts|tsx)": [
Expand Down
1 change: 1 addition & 0 deletions frontend/public/Icons/redis-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion frontend/public/locales/en/titles.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"LOGS_SAVE_VIEWS": "SigNoz | Logs Saved Views",
"TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views",
"DEFAULT": "Open source Observability Platform | SigNoz",
"SHORTCUTS": "SigNoz | Shortcuts"
"SHORTCUTS": "SigNoz | Shortcuts",
"INTEGRATIONS_INSTALLED": "SigNoz | Integrations"
}
15 changes: 15 additions & 0 deletions frontend/src/AppRoutes/pageComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,18 @@ export const WorkspaceBlocked = Loadable(
export const ShortcutsPage = Loadable(
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'),
);

export const InstalledIntegrations = Loadable(
() =>
import(
/* webpackChunkName: "InstalledIntegrations" */ 'pages/IntegrationsModulePage'
),
);

export const IntegrationsMarketPlace = Loadable(
// eslint-disable-next-line sonarjs/no-identical-functions
() =>
import(
/* webpackChunkName: "IntegrationsMarketPlace" */ 'pages/IntegrationsModulePage'
),
);
22 changes: 19 additions & 3 deletions frontend/src/AppRoutes/routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import ROUTES from 'constants/routes';
import Shortcuts from 'pages/Shortcuts/Shortcuts';
import WorkspaceBlocked from 'pages/WorkspaceLocked';
import { RouteProps } from 'react-router-dom';

import {
Expand All @@ -16,6 +14,8 @@ import {
EditRulesPage,
ErrorDetails,
IngestionSettings,
InstalledIntegrations,
IntegrationsMarketPlace,
LicensePage,
ListAllALertsPage,
LiveLogs,
Expand All @@ -35,6 +35,7 @@ import {
ServiceMetricsPage,
ServicesTablePage,
SettingsPage,
ShortcutsPage,
SignupPage,
SomethingWentWrong,
StatusPage,
Expand All @@ -45,6 +46,7 @@ import {
TracesSaveViews,
UnAuthorized,
UsageExplorerPage,
WorkspaceBlocked,
} from './pageComponents';

const routes: AppRoutes[] = [
Expand Down Expand Up @@ -331,10 +333,24 @@ const routes: AppRoutes[] = [
{
path: ROUTES.SHORTCUTS,
exact: true,
component: Shortcuts,
component: ShortcutsPage,
isPrivate: true,
key: 'SHORTCUTS',
},
{
path: ROUTES.INTEGRATIONS_INSTALLED,
exact: true,
component: InstalledIntegrations,
isPrivate: true,
key: 'INTEGRATIONS_INSTALLED',
},
{
path: ROUTES.INTEGRATIONS_MARKETPLACE,
exact: true,
component: IntegrationsMarketPlace,
isPrivate: true,
key: 'INTEGRATIONS_MARKETPLACE',
},
];

export const SUPPORT_ROUTE: AppRoutes = {
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/api/Integrations/getAllIntegrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import axios from 'api';
import { AxiosResponse } from 'axios';
import { AllIntegrationsProps } from 'types/api/integrations/types';

export const getAllIntegrations = (): Promise<
AxiosResponse<AllIntegrationsProps>
> => axios.get(`/integrations`);
11 changes: 11 additions & 0 deletions frontend/src/api/Integrations/getIntegration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import axios from 'api';
import { AxiosResponse } from 'axios';
import {
GetIntegrationPayloadProps,
GetIntegrationProps,
} from 'types/api/integrations/types';

export const getIntegration = (
props: GetIntegrationPayloadProps,
): Promise<AxiosResponse<GetIntegrationProps>> =>
axios.get(`/integrations/${props.integrationId}`);
11 changes: 11 additions & 0 deletions frontend/src/api/Integrations/getIntegrationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import axios from 'api';
import { AxiosResponse } from 'axios';
import {
GetIntegrationPayloadProps,
GetIntegrationStatusProps,
} from 'types/api/integrations/types';

export const getIntegrationStatus = (
props: GetIntegrationPayloadProps,
): Promise<AxiosResponse<GetIntegrationStatusProps>> =>
axios.get(`/integrations/${props.integrationId}/connection_status`);
31 changes: 31 additions & 0 deletions frontend/src/api/Integrations/installIntegration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
InstalledIntegrationsSuccessResponse,
InstallIntegrationKeyProps,
} from 'types/api/integrations/types';

const installIntegration = async (
props: InstallIntegrationKeyProps,
): Promise<
SuccessResponse<InstalledIntegrationsSuccessResponse> | ErrorResponse
> => {
try {
const response = await axios.post('/integrations/install', {
...props,
});

return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};

export default installIntegration;
31 changes: 31 additions & 0 deletions frontend/src/api/Integrations/uninstallIntegration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
UninstallIntegrationProps,
UninstallIntegrationSuccessResponse,
} from 'types/api/integrations/types';

const unInstallIntegration = async (
props: UninstallIntegrationProps,
): Promise<
SuccessResponse<UninstallIntegrationSuccessResponse> | ErrorResponse
> => {
try {
const response = await axios.post('/integrations/uninstall', {
...props,
});

return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};

export default unInstallIntegration;
8 changes: 8 additions & 0 deletions frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable @typescript-eslint/explicit-function-return-type */

import ReactMarkdown from 'react-markdown';
import { CodeProps } from 'react-markdown/lib/ast-to-react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { a11yDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import rehypeRaw from 'rehype-raw';

import CodeCopyBtn from './CodeCopyBtn/CodeCopyBtn';

Expand Down Expand Up @@ -74,6 +76,10 @@ const interpolateMarkdown = (
return interpolatedContent;
};

function CustomTag({ color }: { color: string }): JSX.Element {
return <h1 style={{ color }}>This is custom element</h1>;
}

function MarkdownRenderer({
markdownContent,
variables,
Expand All @@ -85,12 +91,14 @@ function MarkdownRenderer({

return (
<ReactMarkdown
rehypePlugins={[rehypeRaw as any]}
components={{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
a: Link,
pre: Pre,
code: Code,
customtag: CustomTag,
}}
>
{interpolatedMarkdown}
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const ROUTES = {
TRACES_SAVE_VIEWS: '/traces/saved-views',
WORKSPACE_LOCKED: '/workspace-locked',
SHORTCUTS: '/shortcuts',
INTEGRATIONS_BASE: '/integrations',
INTEGRATIONS_INSTALLED: '/integrations/installed',
INTEGRATIONS_MARKETPLACE: '/integrations/marketplace',
} as const;

export default ROUTES;
7 changes: 7 additions & 0 deletions frontend/src/container/SideNav/menuItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ScrollText,
Settings,
Slack,
// Unplug,
UserPlus,
} from 'lucide-react';

Expand Down Expand Up @@ -89,6 +90,11 @@ const menuItems: SidebarItem[] = [
label: 'Alerts',
icon: <BellDot size={16} />,
},
// {
// key: ROUTES.INTEGRATIONS_INSTALLED,
// label: 'Integrations',
// icon: <Unplug size={16} />,
// },
{
key: ROUTES.ALL_ERROR,
label: 'Exceptions',
Expand Down Expand Up @@ -121,6 +127,7 @@ export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record<string, string> = {
[ROUTES.TRACES_EXPLORER]: ROUTES.TRACE,
[ROUTES.TRACE_EXPLORER]: ROUTES.TRACE,
[ROUTES.LOGS_BASE]: ROUTES.LOGS_EXPLORER,
[ROUTES.INTEGRATIONS_BASE]: ROUTES.INTEGRATIONS_INSTALLED,
};

export default menuItems;
3 changes: 3 additions & 0 deletions frontend/src/container/TopNav/DateTimeSelectionV2/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ export const routesToSkip = [
ROUTES.TRACES_EXPLORER,
ROUTES.TRACES_SAVE_VIEWS,
ROUTES.SHORTCUTS,
ROUTES.INTEGRATIONS_BASE,
ROUTES.INTEGRATIONS_INSTALLED,
ROUTES.INTEGRATIONS_MARKETPLACE,
];

export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/hooks/Integrations/useGetAllIntegrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getAllIntegrations } from 'api/Integrations/getAllIntegrations';
import { AxiosError, AxiosResponse } from 'axios';
import { useQuery, UseQueryResult } from 'react-query';
import { AllIntegrationsProps } from 'types/api/integrations/types';

export const useGetAllIntegrations = (): UseQueryResult<
AxiosResponse<AllIntegrationsProps>,
AxiosError
> =>
useQuery<AxiosResponse<AllIntegrationsProps>, AxiosError>({
queryKey: ['Integrations'],
queryFn: () => getAllIntegrations(),
});
18 changes: 18 additions & 0 deletions frontend/src/hooks/Integrations/useGetIntegration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getIntegration } from 'api/Integrations/getIntegration';
import { AxiosError, AxiosResponse } from 'axios';
import { useQuery, UseQueryResult } from 'react-query';
import {
GetIntegrationPayloadProps,
GetIntegrationProps,
} from 'types/api/integrations/types';

export const useGetIntegration = ({
integrationId,
}: GetIntegrationPayloadProps): UseQueryResult<
AxiosResponse<GetIntegrationProps>,
AxiosError
> =>
useQuery<AxiosResponse<GetIntegrationProps>, AxiosError>({
queryKey: ['Integration', integrationId],
queryFn: () => getIntegration({ integrationId }),
});
20 changes: 20 additions & 0 deletions frontend/src/hooks/Integrations/useGetIntegrationStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getIntegrationStatus } from 'api/Integrations/getIntegrationStatus';
import { AxiosError, AxiosResponse } from 'axios';
import { useQuery, UseQueryResult } from 'react-query';
import {
GetIntegrationPayloadProps,
GetIntegrationStatusProps,
} from 'types/api/integrations/types';

export const useGetIntegrationStatus = ({
integrationId,
enabled,
}: GetIntegrationPayloadProps): UseQueryResult<
AxiosResponse<GetIntegrationStatusProps>,
AxiosError
> =>
useQuery<AxiosResponse<GetIntegrationStatusProps>, AxiosError>({
queryKey: ['Integration', integrationId, Date.now()],
queryFn: () => getIntegrationStatus({ integrationId }),
enabled,
});
37 changes: 37 additions & 0 deletions frontend/src/pages/Integrations/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import './Integrations.styles.scss';

import { Color } from '@signozhq/design-tokens';
import { Input, Typography } from 'antd';
import { Search } from 'lucide-react';
import { Dispatch, SetStateAction } from 'react';

interface HeaderProps {
searchTerm: string;
setSearchTerm: Dispatch<SetStateAction<string>>;
}

function Header(props: HeaderProps): JSX.Element {
const { searchTerm, setSearchTerm } = props;

const handleSearch = (e: React.ChangeEvent<HTMLInputElement>): void => {
setSearchTerm(e.target.value);
};
return (
<div className="integrations-header">
<Typography.Title className="title">Integrations</Typography.Title>
<Typography.Text className="subtitle">
Manage Integrations for this workspace
</Typography.Text>

<Input
placeholder="Search for an integration..."
prefix={<Search size={12} color={Color.BG_VANILLA_400} />}
value={searchTerm}
onChange={handleSearch}
className="integrations-search-input"
/>
</div>
);
}

export default Header;
Loading

0 comments on commit 0c41492

Please sign in to comment.