Fixed routes for Service details page (#328)

* Fixed routes for Service details page

* Integrated topis api

* Broker url and other misc fixes
This commit is contained in:
darth-coder00 2021-09-02 09:33:06 +05:30 committed by GitHub
parent 8dcbcbc171
commit 45b2967a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 317 additions and 61 deletions

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AxiosResponse } from 'axios';
import { getURLWithQueryFields } from '../utils/APIUtils';
import APIClient from './index';
export const getTopics: Function = (
serviceName: string,
paging: string,
arrQueryFields: string
): Promise<AxiosResponse> => {
const url = `${getURLWithQueryFields(
`/topics`,
arrQueryFields
)}&service=${serviceName}${paging ? paging : ''}`;
return APIClient.get(url);
};

View File

@ -19,7 +19,10 @@ import classNames from 'classnames';
import { ServiceTypes } from 'Models'; import { ServiceTypes } from 'Models';
import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
import { serviceTypes } from '../../../constants/services.const'; import { serviceTypes } from '../../../constants/services.const';
import { ServiceCategory } from '../../../enums/service.enum'; import {
MessagingServiceType,
ServiceCategory,
} from '../../../enums/service.enum';
import { fromISOString } from '../../../utils/ServiceUtils'; import { fromISOString } from '../../../utils/ServiceUtils';
import { Button } from '../../buttons/Button/Button'; import { Button } from '../../buttons/Button/Button';
import MarkdownWithPreview from '../../common/editor/MarkdownWithPreview'; import MarkdownWithPreview from '../../common/editor/MarkdownWithPreview';
@ -167,8 +170,8 @@ export const AddServiceModal: FunctionComponent<Props> = ({
// const [port, setPort] = useState(parseUrl?.port || ''); // const [port, setPort] = useState(parseUrl?.port || '');
const [database, setDatabase] = useState(parseUrl?.database || ''); const [database, setDatabase] = useState(parseUrl?.database || '');
const [driverClass, setDriverClass] = useState(data?.driverClass || 'jdbc'); const [driverClass, setDriverClass] = useState(data?.driverClass || 'jdbc');
const [broker, setBroker] = useState( const [brokers, setBrokers] = useState(
data?.brokers?.length ? data.brokers[0] : '' data?.brokers?.length ? data.brokers.join(', ') : ''
); );
const [schemaRegistry, setSchemaRegistry] = useState( const [schemaRegistry, setSchemaRegistry] = useState(
data?.schemaRegistry || '' data?.schemaRegistry || ''
@ -188,6 +191,13 @@ export const AddServiceModal: FunctionComponent<Props> = ({
}); });
const [sameNameError, setSameNameError] = useState(false); const [sameNameError, setSameNameError] = useState(false);
const markdownRef = useRef<EditorContentRef>(); const markdownRef = useRef<EditorContentRef>();
const getBrokerUrlPlaceholder = (): string => {
return selectService === MessagingServiceType.PULSAR
? 'eg.: hostname:port'
: 'eg.: hostname1:port1, hostname2:port2';
};
const handleChangeFrequency = ( const handleChangeFrequency = (
event: React.ChangeEvent<HTMLSelectElement> event: React.ChangeEvent<HTMLSelectElement>
) => { ) => {
@ -289,7 +299,7 @@ export const AddServiceModal: FunctionComponent<Props> = ({
{ {
setMsg = { setMsg = {
...setMsg, ...setMsg,
broker: !broker, broker: !brokers,
}; };
} }
@ -329,7 +339,10 @@ export const AddServiceModal: FunctionComponent<Props> = ({
{ {
dataObj = { dataObj = {
...dataObj, ...dataObj,
brokers: [broker], brokers:
selectService === MessagingServiceType.PULSAR
? [brokers]
: brokers.split(',').map((broker) => broker.trim()),
schemaRegistry: schemaRegistry, schemaRegistry: schemaRegistry,
}; };
} }
@ -354,6 +367,7 @@ export const AddServiceModal: FunctionComponent<Props> = ({
className="tw-form-inputs tw-px-3 tw-py-1" className="tw-form-inputs tw-px-3 tw-py-1"
id="url" id="url"
name="url" name="url"
placeholder="eg.: username:password@hostname:port"
type="text" type="text"
value={url} value={url}
onChange={handleValidation} onChange={handleValidation}
@ -416,6 +430,7 @@ export const AddServiceModal: FunctionComponent<Props> = ({
className="tw-form-inputs tw-px-3 tw-py-1" className="tw-form-inputs tw-px-3 tw-py-1"
id="database" id="database"
name="database" name="database"
placeholder="Enter database name"
type="text" type="text"
value={database} value={database}
onChange={(e) => setDatabase(e.target.value)} onChange={(e) => setDatabase(e.target.value)}
@ -460,9 +475,10 @@ export const AddServiceModal: FunctionComponent<Props> = ({
className="tw-form-inputs tw-px-3 tw-py-1" className="tw-form-inputs tw-px-3 tw-py-1"
id="broker" id="broker"
name="broker" name="broker"
placeholder={getBrokerUrlPlaceholder()}
type="text" type="text"
value={broker} value={brokers}
onChange={(e) => setBroker(e.target.value)} onChange={(e) => setBrokers(e.target.value)}
/> />
{showErrorMsg.broker && errorMsg('Broker url is required')} {showErrorMsg.broker && errorMsg('Broker url is required')}
</div> </div>
@ -474,6 +490,7 @@ export const AddServiceModal: FunctionComponent<Props> = ({
className="tw-form-inputs tw-px-3 tw-py-1" className="tw-form-inputs tw-px-3 tw-py-1"
id="schema-registry" id="schema-registry"
name="schema-registry" name="schema-registry"
placeholder="eg.: hostname:port"
type="text" type="text"
value={schemaRegistry} value={schemaRegistry}
onChange={(e) => setSchemaRegistry(e.target.value)} onChange={(e) => setSchemaRegistry(e.target.value)}
@ -546,6 +563,7 @@ export const AddServiceModal: FunctionComponent<Props> = ({
className="tw-form-inputs tw-px-3 tw-py-1" className="tw-form-inputs tw-px-3 tw-py-1"
id="name" id="name"
name="name" name="name"
placeholder="Enter service name"
type="text" type="text"
value={name} value={name}
onChange={handleValidation} onChange={handleValidation}

View File

@ -33,15 +33,15 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
return ( return (
<li key={index}> <li key={index}>
{link.imgSrc ? (
<img
alt=""
className="tw-inline tw-h-5 tw-w-5 tw-mr-2"
src={link.imgSrc}
/>
) : null}
{index < titleLinks.length - 1 ? ( {index < titleLinks.length - 1 ? (
<> <>
{link.imgSrc ? (
<img
alt=""
className="tw-inline tw-h-5 tw-w-5 tw-mr-2"
src={link.imgSrc}
/>
) : null}
<Link className={classes} to={link.url}> <Link className={classes} to={link.url}>
{link.name} {link.name}
</Link> </Link>

View File

@ -36,6 +36,7 @@ const PLACEHOLDER_ROUTE_DATASET_FQN = ':datasetFQN';
const PLACEHOLDER_ROUTE_TOPIC_FQN = ':topicFQN'; const PLACEHOLDER_ROUTE_TOPIC_FQN = ':topicFQN';
const PLACEHOLDER_ROUTE_DATABASE_FQN = ':databaseFQN'; const PLACEHOLDER_ROUTE_DATABASE_FQN = ':databaseFQN';
const PLACEHOLDER_ROUTE_SERVICE_FQN = ':serviceFQN'; const PLACEHOLDER_ROUTE_SERVICE_FQN = ':serviceFQN';
const PLACEHOLDER_ROUTE_SERVICE_TYPE = ':serviceType';
const PLACEHOLDER_ROUTE_SEARCHQUERY = ':searchQuery'; const PLACEHOLDER_ROUTE_SEARCHQUERY = ':searchQuery';
export const pagingObject = { after: '', before: '' }; export const pagingObject = { after: '', before: '' };
@ -101,7 +102,7 @@ export const ROUTES = {
STORE: '/store', STORE: '/store',
FEEDS: '/feeds', FEEDS: '/feeds',
DUMMY: '/dummy', DUMMY: '/dummy',
SERVICE: `/service/${PLACEHOLDER_ROUTE_SERVICE_FQN}`, SERVICE: `/service/${PLACEHOLDER_ROUTE_SERVICE_TYPE}/${PLACEHOLDER_ROUTE_SERVICE_FQN}`,
SERVICES: '/services', SERVICES: '/services',
USERS: '/users', USERS: '/users',
SCORECARD: '/scorecard', SCORECARD: '/scorecard',
@ -129,9 +130,14 @@ export const getDatasetDetailsPath = (
return `${path}${columnName ? `#${columnName}` : ''}`; return `${path}${columnName ? `#${columnName}` : ''}`;
}; };
export const getServiceDetailsPath = (serviceFQN: string) => { export const getServiceDetailsPath = (
serviceFQN: string,
serviceType: string
) => {
let path = ROUTES.SERVICE; let path = ROUTES.SERVICE;
path = path.replace(PLACEHOLDER_ROUTE_SERVICE_FQN, serviceFQN); path = path
.replace(PLACEHOLDER_ROUTE_SERVICE_TYPE, serviceType)
.replace(PLACEHOLDER_ROUTE_SERVICE_FQN, serviceFQN);
return path; return path;
}; };

View File

@ -29,6 +29,7 @@ import redshift from '../assets/img/service-icon-redshift.png';
import snowflakes from '../assets/img/service-icon-snowflakes.png'; import snowflakes from '../assets/img/service-icon-snowflakes.png';
import mysql from '../assets/img/service-icon-sql.png'; import mysql from '../assets/img/service-icon-sql.png';
import plus from '../assets/svg/plus.svg'; import plus from '../assets/svg/plus.svg';
import { ServiceCategory } from '../enums/service.enum';
export const MYSQL = mysql; export const MYSQL = mysql;
export const MSSQL = mssql; export const MSSQL = mssql;
@ -67,3 +68,14 @@ export const servicesDisplayName = {
databaseServices: 'Database Service', databaseServices: 'Database Service',
messagingServices: 'Messaging Service', messagingServices: 'Messaging Service',
}; };
export const routeServiceTypes = [
{
param: 'database',
type: ServiceCategory.DATABASE_SERVICES,
},
{
param: 'messaging',
type: ServiceCategory.MESSAGING_SERVICES,
},
];

View File

@ -50,12 +50,13 @@ declare module 'Models' {
export type ServiceOption = { export type ServiceOption = {
id: string; id: string;
brokers?: Array<string>;
description: string; description: string;
ingestionSchedule?: { ingestionSchedule?: {
repeatFrequency: string; repeatFrequency: string;
startDate: string; startDate: string;
}; };
jdbc: { connectionUrl: string; driverClass: string }; jdbc?: { connectionUrl: string; driverClass: string };
name: string; name: string;
serviceType: string; serviceType: string;
}; };

View File

@ -115,7 +115,10 @@ const DatabaseDetails: FunctionComponent = () => {
{ {
name: resService.data.name, name: resService.data.name,
url: resService.data.name url: resService.data.name
? getServiceDetailsPath(resService.data.name) ? getServiceDetailsPath(
resService.data.name,
resService.data.serviceType
)
: '', : '',
imgSrc: resService.data.serviceType imgSrc: resService.data.serviceType
? serviceTypeLogo(resService.data.serviceType) ? serviceTypeLogo(resService.data.serviceType)

View File

@ -327,7 +327,10 @@ const MyDataDetailsPage = () => {
{ {
name: resService.data.name, name: resService.data.name,
url: resService.data.name url: resService.data.name
? getServiceDetailsPath(resService.data.name) ? getServiceDetailsPath(
resService.data.name,
resService.data.serviceType
)
: '', : '',
imgSrc: resService.data.serviceType imgSrc: resService.data.serviceType
? serviceTypeLogo(resService.data.serviceType) ? serviceTypeLogo(resService.data.serviceType)

View File

@ -19,27 +19,38 @@ import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { isNull, isUndefined } from 'lodash'; import { isNull, isUndefined } from 'lodash';
import { Database, Paging, ServiceOption } from 'Models'; import { Database, Paging, ServiceOption } from 'Models';
import React, { FunctionComponent, useEffect, useState } from 'react'; import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom'; import { Link, useParams } from 'react-router-dom';
import { getDatabases } from '../../axiosAPIs/databaseAPI'; import { getDatabases } from '../../axiosAPIs/databaseAPI';
import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI'; import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI';
import { getTopics } from '../../axiosAPIs/topicsAPI';
import NextPrevious from '../../components/common/next-previous/NextPrevious'; import NextPrevious from '../../components/common/next-previous/NextPrevious';
import PopOver from '../../components/common/popover/PopOver';
import RichTextEditorPreviewer from '../../components/common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from '../../components/common/rich-text-editor/RichTextEditorPreviewer';
import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-breadcrumb.component'; import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface'; import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import PageContainer from '../../components/containers/PageContainer'; import PageContainer from '../../components/containers/PageContainer';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import { ModalWithMarkdownEditor } from '../../components/Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import { ModalWithMarkdownEditor } from '../../components/Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import Tags from '../../components/tags/tags';
import { pagingObject } from '../../constants/constants'; import { pagingObject } from '../../constants/constants';
import { ServiceCategory } from '../../enums/service.enum';
import { Topic } from '../../generated/entity/data/topic';
import useToastContext from '../../hooks/useToastContext'; import useToastContext from '../../hooks/useToastContext';
import { isEven } from '../../utils/CommonUtils'; import { isEven } from '../../utils/CommonUtils';
import { getFrequencyTime, serviceTypeLogo } from '../../utils/ServiceUtils'; import {
getFrequencyTime,
getServiceCategoryFromType,
serviceTypeLogo,
} from '../../utils/ServiceUtils';
import SVGIcons from '../../utils/SvgUtils'; import SVGIcons from '../../utils/SvgUtils';
import { getUsagePercentile } from '../../utils/TableUtils'; import { getUsagePercentile } from '../../utils/TableUtils';
const ServicePage: FunctionComponent = () => { const ServicePage: FunctionComponent = () => {
const { serviceFQN } = useParams() as Record<string, string>; const { serviceFQN, serviceType } = useParams() as Record<string, string>;
const [serviceName] = useState('databaseServices'); const [serviceName, setServiceName] = useState(
getServiceCategoryFromType(serviceType)
);
const [slashedTableName, setSlashedTableName] = useState< const [slashedTableName, setSlashedTableName] = useState<
TitleBreadcrumbProps['titleLinks'] TitleBreadcrumbProps['titleLinks']
>([]); >([]);
@ -47,7 +58,7 @@ const ServicePage: FunctionComponent = () => {
const [description, setDescription] = useState(''); const [description, setDescription] = useState('');
const [serviceDetails, setServiceDetails] = useState<ServiceOption>(); const [serviceDetails, setServiceDetails] = useState<ServiceOption>();
const [data, setData] = useState<Array<Database>>([]); const [data, setData] = useState<Array<Database>>([]);
const [isLoading, setIsloading] = useState(false); const [isLoading, setIsloading] = useState(true);
const [paging, setPaging] = useState<Paging>(pagingObject); const [paging, setPaging] = useState<Paging>(pagingObject);
const showToast = useToastContext(); const showToast = useToastContext();
@ -70,6 +81,179 @@ const ServicePage: FunctionComponent = () => {
}); });
}; };
const fetchTopics = (paging?: string) => {
setIsloading(true);
getTopics(serviceFQN, paging, ['owner', 'service', 'tags'])
.then((res: AxiosResponse) => {
if (res.data.data) {
setData(res.data.data);
setPaging(res.data.paging);
setIsloading(false);
} else {
setData([]);
setPaging(pagingObject);
setIsloading(false);
}
})
.catch(() => {
setIsloading(false);
});
};
const getOtherDetails = (paging?: string) => {
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES: {
fetchDatabases(paging);
break;
}
case ServiceCategory.MESSAGING_SERVICES: {
fetchTopics(paging);
break;
}
default:
break;
}
};
const getOptionalFields = (): JSX.Element => {
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES: {
return (
<span>
<span className="tw-text-grey-muted tw-font-normal">
Driver Class :
</span>{' '}
<span className="tw-pl-1tw-font-normal ">
{serviceDetails?.jdbc?.driverClass || '--'}
</span>
<span className="tw-mx-3 tw-inline-block tw-text-gray-400"></span>
</span>
);
}
case ServiceCategory.MESSAGING_SERVICES: {
return (
<span>
<span className="tw-text-grey-muted tw-font-normal">Brokers :</span>{' '}
<span className="tw-pl-1tw-font-normal ">
{serviceDetails?.brokers?.length ? (
<>
{serviceDetails.brokers.slice(0, 3).join(', ')}
{serviceDetails.brokers.length > 3 ? (
<PopOver
html={
<div className="tw-text-left">
{serviceDetails.brokers
.slice(3)
.map((broker, index) => (
<Fragment key={index}>
<span className="tw-block tw-py-1">
{broker}
</span>
</Fragment>
))}
</div>
}
position="bottom"
theme="light"
trigger="click">
<span className="show-more tw-ml-1">...</span>
</PopOver>
) : null}
</>
) : (
'--'
)}
</span>
<span className="tw-mx-3 tw-inline-block tw-text-gray-400"></span>
</span>
);
}
default: {
return <></>;
}
}
};
const getTableHeaders = (): JSX.Element => {
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES: {
return (
<>
<th className="tableHead-cell">Database Name</th>
<th className="tableHead-cell">Description</th>
<th className="tableHead-cell">Owner</th>
<th className="tableHead-cell">Usage</th>
</>
);
}
case ServiceCategory.MESSAGING_SERVICES: {
return (
<>
<th className="tableHead-cell">Topic Name</th>
<th className="tableHead-cell">Description</th>
<th className="tableHead-cell">Owner</th>
<th className="tableHead-cell">Tags</th>
</>
);
}
default:
return <></>;
}
};
const getOptionalTableCells = (data: Database | Topic) => {
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES: {
const database = data as Database;
return (
<td className="tableBody-cell">
<p>
{getUsagePercentile(
database.usageSummary.weeklyStats.percentileRank
)}
</p>
</td>
);
}
case ServiceCategory.MESSAGING_SERVICES: {
const topic = data as Topic;
return (
<td className="tableBody-cell">
{topic.tags && topic.tags?.length > 0
? topic.tags.map((tag, tagIndex) => (
<PopOver
key={tagIndex}
position="top"
size="small"
title={tag.labelType}
trigger="mouseenter">
<Tags
className="tw-bg-gray-200"
tag={`#${
tag.tagFQN?.startsWith('Tier.Tier')
? tag.tagFQN.split('.')[1]
: tag.tagFQN
}`}
/>
</PopOver>
))
: '--'}
</td>
);
}
default:
return <></>;
}
};
useEffect(() => {
setServiceName(getServiceCategoryFromType(serviceType));
}, [serviceType]);
useEffect(() => { useEffect(() => {
getServiceByFQN(serviceName, serviceFQN).then( getServiceByFQN(serviceName, serviceFQN).then(
(resService: AxiosResponse) => { (resService: AxiosResponse) => {
@ -84,13 +268,10 @@ const ServicePage: FunctionComponent = () => {
activeTitle: true, activeTitle: true,
}, },
]); ]);
getOtherDetails();
} }
); );
}, []); }, [serviceFQN, serviceName]);
useEffect(() => {
fetchDatabases();
}, [serviceFQN]);
const onCancel = () => { const onCancel = () => {
setIsEdit(false); setIsEdit(false);
@ -129,7 +310,7 @@ const ServicePage: FunctionComponent = () => {
const pagingString = `&${cursorType}=${ const pagingString = `&${cursorType}=${
paging[cursorType as keyof typeof paging] paging[cursorType as keyof typeof paging]
}`; }`;
fetchDatabases(pagingString); getOtherDetails(pagingString);
}; };
return ( return (
@ -142,17 +323,7 @@ const ServicePage: FunctionComponent = () => {
<TitleBreadcrumb titleLinks={slashedTableName} /> <TitleBreadcrumb titleLinks={slashedTableName} />
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1"> <div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1">
<span> {getOptionalFields()}
<span className="tw-text-grey-muted tw-font-normal">
Driver Class :
</span>{' '}
<span className="tw-pl-1tw-font-normal ">
{serviceDetails?.jdbc.driverClass || '--'}
</span>
<span className="tw-mx-3 tw-inline-block tw-text-gray-400">
</span>
</span>
<span> <span>
<span className="tw-text-grey-muted tw-font-normal"> <span className="tw-text-grey-muted tw-font-normal">
Ingestion : Ingestion :
@ -163,7 +334,7 @@ const ServicePage: FunctionComponent = () => {
? getFrequencyTime( ? getFrequencyTime(
serviceDetails.ingestionSchedule.repeatFrequency serviceDetails.ingestionSchedule.repeatFrequency
) )
: 'N/A'} : '--'}
</span> </span>
<span className="tw-mx-3 tw-inline-block tw-text-gray-400"> <span className="tw-mx-3 tw-inline-block tw-text-gray-400">
@ -223,12 +394,7 @@ const ServicePage: FunctionComponent = () => {
className="tw-bg-white tw-w-full tw-mb-4" className="tw-bg-white tw-w-full tw-mb-4"
data-testid="database-tables"> data-testid="database-tables">
<thead> <thead>
<tr className="tableHead-row"> <tr className="tableHead-row">{getTableHeaders()}</tr>
<th className="tableHead-cell">Database Name</th>
<th className="tableHead-cell">Description</th>
<th className="tableHead-cell">Owner</th>
<th className="tableHead-cell">Usage</th>
</tr>
</thead> </thead>
<tbody className="tableBody"> <tbody className="tableBody">
{data.length > 0 ? ( {data.length > 0 ? (
@ -259,13 +425,7 @@ const ServicePage: FunctionComponent = () => {
<td className="tableBody-cell"> <td className="tableBody-cell">
<p>{database?.owner?.name || '--'}</p> <p>{database?.owner?.name || '--'}</p>
</td> </td>
<td className="tableBody-cell"> {getOptionalTableCells(database)}
<p>
{getUsagePercentile(
database.usageSummary.weeklyStats.percentileRank
)}
</p>
</td>
</tr> </tr>
)) ))
) : ( ) : (

View File

@ -292,7 +292,11 @@ const ServicesPage = () => {
className="tw-card tw-flex tw-py-2 tw-px-3 tw-justify-between tw-text-grey-muted" className="tw-card tw-flex tw-py-2 tw-px-3 tw-justify-between tw-text-grey-muted"
key={index}> key={index}>
<div className="tw-flex-auto"> <div className="tw-flex-auto">
<Link to={getServiceDetailsPath(service.name)}> <Link
to={getServiceDetailsPath(
service.name,
service.serviceType
)}>
<button> <button>
<h6 className="tw-text-base tw-text-grey-body tw-font-medium"> <h6 className="tw-text-base tw-text-grey-body tw-font-medium">
{service.name} {service.name}
@ -318,7 +322,7 @@ const ServicesPage = () => {
? getFrequencyTime( ? getFrequencyTime(
service.ingestionSchedule.repeatFrequency service.ingestionSchedule.repeatFrequency
) )
: 'N/A'} : '--'}
</span> </span>
</div> </div>
<div className=""> <div className="">
@ -359,9 +363,9 @@ const ServicesPage = () => {
</div> </div>
))} ))}
<div <div
className="tw-cursor-pointer tw-card tw-flex tw-flex-col tw-justify-center tw-items-center tw-py-2" className="tw-cursor-pointer tw-card tw-flex tw-flex-col tw-justify-center tw-items-center tw-py-6"
onClick={() => handleAddService()}> onClick={() => handleAddService()}>
<img alt="" src={PLUS} /> <img alt="Add service" src={PLUS} />
<p className="tw-text-base tw-font-normal tw-mt-4"> <p className="tw-text-base tw-font-normal tw-mt-4">
Add new {servicesDisplayName[serviceName]} Add new {servicesDisplayName[serviceName]}
</p> </p>

View File

@ -1,5 +1,5 @@
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { ServiceCollection, ServiceData } from 'Models'; import { ServiceCollection, ServiceData, ServiceTypes } from 'Models';
import { getServiceDetails, getServices } from '../axiosAPIs/serviceAPI'; import { getServiceDetails, getServices } from '../axiosAPIs/serviceAPI';
import { ServiceDataObj } from '../components/Modals/AddServiceModal/AddServiceModal'; import { ServiceDataObj } from '../components/Modals/AddServiceModal/AddServiceModal';
import { import {
@ -12,6 +12,7 @@ import {
POSTGRES, POSTGRES,
PULSAR, PULSAR,
REDSHIFT, REDSHIFT,
serviceTypes,
SERVICE_DEFAULT, SERVICE_DEFAULT,
SNOWFLAKE, SNOWFLAKE,
} from '../constants/services.const'; } from '../constants/services.const';
@ -137,3 +138,18 @@ export const getAllServices = (): Promise<Array<ServiceDataObj>> => {
}); });
}); });
}; };
export const getServiceCategoryFromType = (
type: string
): ServiceTypes | undefined => {
let serviceCategory;
for (const category in serviceTypes) {
if (serviceTypes[category as ServiceTypes].includes(type)) {
serviceCategory = category as ServiceTypes;
break;
}
}
return serviceCategory;
};