diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/service.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/service.ts index 0442b7326b2..31ce5b975b0 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/service.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/service.ts @@ -10,6 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { EntityTypeEndpoint } from '../support/entity/Entity.interface'; import { uuid } from '../utils/common'; import { GlobalSettingOptions, ServiceTypes } from './settings'; @@ -25,6 +26,11 @@ export const SERVICE_TYPE = { StoredProcedure: GlobalSettingOptions.STORED_PROCEDURES, ApiService: GlobalSettingOptions.APIS, }; +export const FollowSupportedServices = [ + EntityTypeEndpoint.DatabaseService, + EntityTypeEndpoint.DatabaseSchema, + EntityTypeEndpoint.Database, +]; export const VISIT_SERVICE_PAGE_DETAILS = { [SERVICE_TYPE.Database]: { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ServiceEntity.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ServiceEntity.spec.ts index 9300808add2..f138d821fd1 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ServiceEntity.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ServiceEntity.spec.ts @@ -12,6 +12,7 @@ */ import { test } from '@playwright/test'; import { CustomPropertySupportedEntityList } from '../../constant/customProperty'; +import { FollowSupportedServices } from '../../constant/service'; import { ApiCollectionClass } from '../../support/entity/ApiCollectionClass'; import { DatabaseClass } from '../../support/entity/DatabaseClass'; import { DatabaseSchemaClass } from '../../support/entity/DatabaseSchemaClass'; @@ -158,6 +159,14 @@ entities.forEach((EntityClass) => { await afterAction(); }); } + if (FollowSupportedServices.includes(entity.endpoint)) { + test(`Follow & Un-follow entity for Database Entity`, async ({ + page, + }) => { + const entityName = entity.entityResponseData?.['displayName']; + await entity.followUnfollowEntity(page, entityName); + }); + } test(`Update displayName`, async ({ page }) => { await entity.renameEntity(page, entity.entity.name); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx index ca0c390c497..e5b065fdbec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx @@ -144,16 +144,15 @@ export const DataAssetsHeader = ({ ) : null; }, [dataAsset]); - const excludeEntityService = useMemo( - () => - [ - EntityType.DATABASE, - EntityType.DATABASE_SCHEMA, - EntityType.API_COLLECTION, - ...SERVICE_TYPES, - ].includes(entityType), - [entityType] - ); + const excludeEntityService = useMemo(() => { + const filteredServiceTypes = SERVICE_TYPES.filter( + (type) => type !== EntityType.DATABASE_SERVICE + ); + + return [EntityType.API_COLLECTION, ...filteredServiceTypes].includes( + entityType + ); + }, [entityType]); const hasFollowers = 'followers' in dataAsset; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.test.tsx index d300e6f9f1e..212f4ac00f0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.test.tsx @@ -292,7 +292,7 @@ describe('Test DatabaseDetails page', () => { ); expect(getDatabaseDetailsByFQN).toHaveBeenCalledWith('bigquery.shopify', { - fields: 'owners,tags,domain,votes,extension,dataProducts', + fields: 'owners,tags,domain,votes,extension,dataProducts,followers', include: 'all', }); expect(entityHeader).toBeInTheDocument(); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx index e59503bd09e..89bb6d2d05e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx @@ -37,6 +37,7 @@ import { EntityName } from '../../components/Modals/EntityNameModal/EntityNameMo import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1'; import { ROUTES } from '../../constants/constants'; import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants'; +import { GlobalSettingOptions } from '../../constants/GlobalSettings.constants'; import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider'; import { OperationPermission, @@ -54,13 +55,16 @@ import { Database } from '../../generated/entity/data/database'; import { PageType } from '../../generated/system/ui/uiCustomization'; import { Include } from '../../generated/type/include'; import { useLocationSearch } from '../../hooks/LocationSearch/useLocationSearch'; +import { useApplicationStore } from '../../hooks/useApplicationStore'; import { useCustomPages } from '../../hooks/useCustomPages'; import { useFqn } from '../../hooks/useFqn'; import { FeedCounts } from '../../interface/feed.interface'; import { + addFollowers, getDatabaseDetailsByFQN, getDatabaseSchemas, patchDatabaseDetails, + removeFollowers, restoreDatabase, updateDatabaseVotes, } from '../../rest/databaseAPI'; @@ -111,11 +115,14 @@ const DatabaseDetails: FunctionComponent = () => { const isMounting = useRef(true); const [isTabExpanded, setIsTabExpanded] = useState(false); - const { version: currentVersion, deleted } = useMemo( - () => database, - [database] - ); + const { + version: currentVersion, + deleted, + id: databaseId, + } = useMemo(() => database, [database]); + const { currentUser } = useApplicationStore(); + const USERId = currentUser?.id ?? ''; const tier = getTierTags(database?.tags ?? []); const [databasePermission, setDatabasePermission] = @@ -177,7 +184,15 @@ const DatabaseDetails: FunctionComponent = () => { const getDetailsByFQN = () => { setIsDatabaseDetailsLoading(true); getDatabaseDetailsByFQN(decodedDatabaseFQN, { - fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${TabSpecificField.DOMAIN},${TabSpecificField.VOTES},${TabSpecificField.EXTENSION},${TabSpecificField.DATA_PRODUCTS}`, + fields: [ + TabSpecificField.OWNERS, + TabSpecificField.TAGS, + TabSpecificField.DOMAIN, + TabSpecificField.VOTES, + TabSpecificField.EXTENSION, + TabSpecificField.DATA_PRODUCTS, + TabSpecificField.FOLLOWERS, + ].join(','), include: Include.All, }) .then((res) => { @@ -325,6 +340,15 @@ const DatabaseDetails: FunctionComponent = () => { }); }; + const { isFollowing, followers = [] } = useMemo( + () => ({ + isFollowing: database?.followers?.some( + ({ id }) => id === currentUser?.id + ), + followers: database?.followers ?? [], + }), + [database, currentUser] + ); const handleRestoreDatabase = useCallback(async () => { try { const { version: newVersion } = await restoreDatabase(database.id ?? ''); @@ -431,6 +455,64 @@ const DatabaseDetails: FunctionComponent = () => { showErrorToast(error as AxiosError); } }; + const followDatabase = useCallback(async () => { + try { + const res = await addFollowers( + databaseId, + USERId ?? '', + GlobalSettingOptions.DATABASES + ); + const { newValue } = res.changeDescription.fieldsAdded[0]; + const newFollowers = [...(followers ?? []), ...newValue]; + setDatabase((prev) => { + if (!prev) { + return prev; + } + + return { ...prev, followers: newFollowers }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-follow-error', { + entity: getEntityName(database), + }) + ); + } + }, [USERId, databaseId]); + const unfollowDatabase = useCallback(async () => { + try { + const res = await removeFollowers( + databaseId, + USERId, + GlobalSettingOptions.DATABASES + ); + const { oldValue } = res.changeDescription.fieldsDeleted[0]; + setDatabase((pre) => { + if (!pre) { + return pre; + } + + return { + ...pre, + followers: pre.followers?.filter( + (follower) => follower.id !== oldValue[0].id + ), + }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-unfollow-error', { + entity: getEntityName(database), + }) + ); + } + }, [USERId, database]); + + const handleFollowClick = useCallback(async () => { + isFollowing ? await unfollowDatabase() : await followDatabase(); + }, [isFollowing, unfollowDatabase, followDatabase]); const toggleTabExpanded = () => { setIsTabExpanded(!isTabExpanded); @@ -471,6 +553,7 @@ const DatabaseDetails: FunctionComponent = () => { openTaskCount={feedCount.openTaskCount} permissions={databasePermission} onDisplayNameUpdate={handleUpdateDisplayName} + onFollowClick={handleFollowClick} onOwnerUpdate={handleUpdateOwner} onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)} onRestoreDataAsset={handleRestoreDatabase} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx index c66603062c5..97259eb9a93 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx @@ -40,6 +40,7 @@ import { ROUTES, } from '../../constants/constants'; import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants'; +import { GlobalSettingOptions } from '../../constants/GlobalSettings.constants'; import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider'; import { OperationPermission, @@ -56,13 +57,16 @@ import { Tag } from '../../generated/entity/classification/tag'; import { DatabaseSchema } from '../../generated/entity/data/databaseSchema'; import { PageType } from '../../generated/system/ui/page'; import { Include } from '../../generated/type/include'; +import { useApplicationStore } from '../../hooks/useApplicationStore'; import { useCustomPages } from '../../hooks/useCustomPages'; import { useFqn } from '../../hooks/useFqn'; import { useTableFilters } from '../../hooks/useTableFilters'; import { FeedCounts } from '../../interface/feed.interface'; import { + addFollowers, getDatabaseSchemaDetailsByFQN, patchDatabaseSchemaDetails, + removeFollowers, restoreDatabaseSchema, updateDatabaseSchemaVotes, } from '../../rest/databaseAPI'; @@ -85,6 +89,8 @@ import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; const DatabaseSchemaPage: FunctionComponent = () => { const { t } = useTranslation(); const { getEntityPermissionByFqn } = usePermissionProvider(); + const { currentUser } = useApplicationStore(); + const USERId = currentUser?.id ?? ''; const { setFilters, filters } = useTableFilters(INITIAL_TABLE_FILTERS); const { tab: activeTab = EntityTabs.TABLE } = @@ -111,6 +117,15 @@ const DatabaseSchemaPage: FunctionComponent = () => { const [updateProfilerSetting, setUpdateProfilerSetting] = useState(false); + const { isFollowing, followers = [] } = useMemo( + () => ({ + isFollowing: databaseSchema?.followers?.some( + ({ id }) => id === currentUser?.id + ), + followers: databaseSchema?.followers ?? [], + }), + [currentUser, databaseSchema] + ); const extraDropdownContent = useMemo( () => entityUtilClassBase.getManageExtraOptions( @@ -170,8 +185,16 @@ const DatabaseSchemaPage: FunctionComponent = () => { const response = await getDatabaseSchemaDetailsByFQN( decodedDatabaseSchemaFQN, { - // eslint-disable-next-line max-len - fields: `${TabSpecificField.OWNERS},${TabSpecificField.USAGE_SUMMARY},${TabSpecificField.TAGS},${TabSpecificField.DOMAIN},${TabSpecificField.VOTES},${TabSpecificField.EXTENSION},${TabSpecificField.DATA_PRODUCTS}`, + fields: [ + TabSpecificField.OWNERS, + TabSpecificField.USAGE_SUMMARY, + TabSpecificField.TAGS, + TabSpecificField.DOMAIN, + TabSpecificField.VOTES, + TabSpecificField.EXTENSION, + TabSpecificField.FOLLOWERS, + TabSpecificField.DATA_PRODUCTS, + ].join(','), include: Include.All, } ); @@ -500,6 +523,65 @@ const DatabaseSchemaPage: FunctionComponent = () => { checkIfExpandViewSupported(tabs[0], activeTab, PageType.DatabaseSchema), [tabs[0], activeTab] ); + const followSchema = useCallback(async () => { + try { + const res = await addFollowers( + databaseSchemaId, + USERId ?? '', + GlobalSettingOptions.DATABASE_SCHEMA + ); + const { newValue } = res.changeDescription.fieldsAdded[0]; + const newFollowers = [...(followers ?? []), ...newValue]; + setDatabaseSchema((prev) => { + if (!prev) { + return prev; + } + + return { ...prev, followers: newFollowers }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-follow-error', { + entity: getEntityName(databaseSchema), + }) + ); + } + }, [USERId, databaseSchemaId]); + const unFollowSchema = useCallback(async () => { + try { + const res = await removeFollowers( + databaseSchemaId, + USERId, + GlobalSettingOptions.DATABASE_SCHEMA + ); + const { oldValue } = res.changeDescription.fieldsDeleted[0]; + setDatabaseSchema((pre) => { + if (!pre) { + return pre; + } + + return { + ...pre, + followers: pre.followers?.filter( + (follower) => follower.id !== oldValue[0].id + ), + }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-unfollow-error', { + entity: getEntityName(databaseSchema), + }) + ); + } + }, [USERId, databaseSchemaId]); + + const handleFollowClick = useCallback(async () => { + isFollowing ? await unFollowSchema() : await followSchema(); + }, [isFollowing, unFollowSchema, followSchema]); + if (isPermissionsLoading) { return ; } @@ -542,6 +624,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { extraDropdownContent={extraDropdownContent} permissions={databaseSchemaPermission} onDisplayNameUpdate={handleUpdateDisplayName} + onFollowClick={handleFollowClick} onOwnerUpdate={handleUpdateOwner} onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)} onRestoreDataAsset={handleRestoreDatabaseSchema} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx index c3c0050dae4..9dda0b9265d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx @@ -198,6 +198,7 @@ const API_FIELDS = [ 'domain', 'votes', 'extension', + 'followers', 'dataProducts', ]; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx index 7aa91d8a3cc..1ad1c0de7d5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx @@ -94,8 +94,10 @@ import { getPipelines } from '../../rest/pipelineAPI'; import { searchQuery } from '../../rest/searchAPI'; import { getSearchIndexes } from '../../rest/SearchIndexAPI'; import { + addServiceFollower, getServiceByFQN, patchService, + removeServiceFollower, restoreService, } from '../../rest/serviceAPI'; import { getContainers } from '../../rest/storageAPI'; @@ -173,7 +175,7 @@ const ServiceDetailsPage: FunctionComponent = () => { const [workflowStatesData, setWorkflowStatesData] = useState(); const [isWorkflowStatusLoading, setIsWorkflowStatusLoading] = useState(true); - + const USERId = currentUser?.id ?? ''; const { paging: collateAgentPaging, pageSize: collateAgentPageSize, @@ -225,6 +227,15 @@ const ServiceDetailsPage: FunctionComponent = () => { const [isCollateAgentLoading, setIsCollateAgentLoading] = useState(false); const [collateAgentsList, setCollateAgentsList] = useState([]); + const { isFollowing, followers = [] } = useMemo( + () => ({ + isFollowing: serviceDetails?.followers?.some( + ({ id }) => id === currentUser?.id + ), + followers: serviceDetails?.followers ?? [], + }), + [serviceDetails, currentUser] + ); const { CollateAIAgentsWidget } = useMemo( () => serviceUtilClassBase.getAgentsTabWidgets(), [] @@ -301,10 +312,11 @@ const ServiceDetailsPage: FunctionComponent = () => { return shouldTestConnection(serviceCategory); }, [serviceCategory]); - const { version: currentVersion, deleted } = useMemo( - () => serviceDetails, - [serviceDetails] - ); + const { + version: currentVersion, + deleted, + id: serviceId, + } = useMemo(() => serviceDetails, [serviceDetails]); const fetchServicePermission = useCallback(async () => { setIsLoading(true); @@ -719,8 +731,10 @@ const ServiceDetailsPage: FunctionComponent = () => { decodedServiceFQN, { fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${ - isMetadataService ? '' : TabSpecificField.DATA_PRODUCTS - },${isMetadataService ? '' : TabSpecificField.DOMAIN}`, + TabSpecificField.FOLLOWERS + },${isMetadataService ? '' : TabSpecificField.DATA_PRODUCTS},${ + isMetadataService ? '' : TabSpecificField.DOMAIN + }`, include: Include.All, } ); @@ -738,6 +752,55 @@ const ServiceDetailsPage: FunctionComponent = () => { } }, [serviceCategory, decodedServiceFQN, isMetadataService]); + const followService = useCallback(async () => { + try { + const res = await addServiceFollower(serviceId, USERId ?? ''); + const { newValue } = res.changeDescription.fieldsAdded[0]; + const newFollowers = [...(followers ?? []), ...newValue]; + setServiceDetails((prev) => { + if (!prev) { + return prev; + } + + return { ...prev, followers: newFollowers }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-follow-error', { + entity: getEntityName(serviceDetails), + }) + ); + } + }, [USERId, serviceId]); + const unFollowService = useCallback(async () => { + try { + const res = await removeServiceFollower(serviceId, USERId); + const { oldValue } = res.changeDescription.fieldsDeleted[0]; + setServiceDetails((pre) => { + if (!pre) { + return pre; + } + + return { + ...pre, + followers: pre.followers?.filter( + (follower) => follower.id !== oldValue[0].id + ), + }; + }); + } catch (error) { + showErrorToast( + error as AxiosError, + t('server.entity-unfollow-error', { + entity: getEntityName(serviceDetails), + }) + ); + } + }, [USERId, serviceId]); + const handleFollowClick = useCallback(async () => { + isFollowing ? await unFollowService() : await followService(); + }, [isFollowing, unFollowService, followService]); const handleUpdateDisplayName = useCallback( async (data: EntityName) => { if (isEmpty(serviceDetails)) { @@ -1427,6 +1490,7 @@ const ServiceDetailsPage: FunctionComponent = () => { permissions={servicePermission} showDomain={!isMetadataService} onDisplayNameUpdate={handleUpdateDisplayName} + onFollowClick={handleFollowClick} onOwnerUpdate={handleUpdateOwner} onRestoreDataAsset={handleRestoreService} onTierUpdate={handleUpdateTier} diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts index 2192b5c94bb..6b1e21ad7ac 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts @@ -15,11 +15,13 @@ import { AxiosResponse } from 'axios'; import { Operation } from 'fast-json-patch'; import { PagingWithoutTotal, RestoreRequestType } from 'Models'; import { QueryVote } from '../components/Database/TableQueries/TableQueries.interface'; +import { APPLICATION_JSON_CONTENT_TYPE_HEADER } from '../constants/constants'; import { Database, DatabaseProfilerConfig as ProfilerConfig, } from '../generated/entity/data/database'; import { DatabaseSchema } from '../generated/entity/data/databaseSchema'; +import { EntityReference } from '../generated/entity/type'; import { EntityHistory } from '../generated/type/entityHistory'; import { Include } from '../generated/type/include'; import { Paging } from '../generated/type/paging'; @@ -86,6 +88,35 @@ export const patchDatabaseSchemaDetails = async ( return response.data; }; +export const addFollowers = async ( + id: string, + userId: string, + path: string +) => { + const response = await APIClient.put< + string, + AxiosResponse<{ + changeDescription: { fieldsAdded: { newValue: EntityReference[] }[] }; + }> + >(`${path}/${id}/followers`, userId, APPLICATION_JSON_CONTENT_TYPE_HEADER); + + return response.data; +}; + +export const removeFollowers = async ( + id: string, + userId: string, + path: string +) => { + const response = await APIClient.delete< + string, + AxiosResponse<{ + changeDescription: { fieldsDeleted: { oldValue: EntityReference[] }[] }; + }> + >(`${path}/${id}/followers/${userId}`, APPLICATION_JSON_CONTENT_TYPE_HEADER); + + return response.data; +}; export const getDatabaseSchemas = async ({ include = Include.NonDeleted, databaseName, diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts index 31687bba462..fe94a05b218 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts @@ -14,9 +14,13 @@ import { AxiosResponse } from 'axios'; import { RestoreRequestType, ServicesUpdateRequest } from 'Models'; import { WILD_CARD_CHAR } from '../constants/char.constants'; -import { PAGE_SIZE } from '../constants/constants'; +import { + APPLICATION_JSON_CONTENT_TYPE_HEADER, + PAGE_SIZE, +} from '../constants/constants'; import { TabSpecificField } from '../enums/entity.enum'; import { SearchIndex } from '../enums/search.enum'; +import { EntityReference } from '../generated/entity/type'; import { EntityHistory } from '../generated/type/entityHistory'; import { Include } from '../generated/type/include'; import { ListParams } from '../interface/API.interface'; @@ -97,6 +101,34 @@ export const postService = async ( return response.data; }; +export const addServiceFollower = async (id: string, userId: string) => { + const response = await APIClient.put< + string, + AxiosResponse<{ + changeDescription: { fieldsAdded: { newValue: EntityReference[] }[] }; + }> + >( + `/services/databaseServices/${id}/followers`, + userId, + APPLICATION_JSON_CONTENT_TYPE_HEADER + ); + + return response.data; +}; + +export const removeServiceFollower = async (id: string, userId: string) => { + const response = await APIClient.delete< + string, + AxiosResponse<{ + changeDescription: { fieldsDeleted: { oldValue: EntityReference[] }[] }; + }> + >( + `/services/databaseServices/${id}/followers/${userId}`, + APPLICATION_JSON_CONTENT_TYPE_HEADER + ); + + return response.data; +}; export const patchService = async ( serviceCat: string, id: string,