mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-16 04:57:11 +00:00
Fix : database and shcema page followers (#21148)
* database and shcema page followers * follow unffolow database * fixed tests * fixed test * fixed comments * removed playwright file * cleanup code * cleanup code * fixed sonar
This commit is contained in:
parent
54d40535cf
commit
97c8cc06ab
@ -10,6 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import { EntityTypeEndpoint } from '../support/entity/Entity.interface';
|
||||||
import { uuid } from '../utils/common';
|
import { uuid } from '../utils/common';
|
||||||
import { GlobalSettingOptions, ServiceTypes } from './settings';
|
import { GlobalSettingOptions, ServiceTypes } from './settings';
|
||||||
|
|
||||||
@ -25,6 +26,11 @@ export const SERVICE_TYPE = {
|
|||||||
StoredProcedure: GlobalSettingOptions.STORED_PROCEDURES,
|
StoredProcedure: GlobalSettingOptions.STORED_PROCEDURES,
|
||||||
ApiService: GlobalSettingOptions.APIS,
|
ApiService: GlobalSettingOptions.APIS,
|
||||||
};
|
};
|
||||||
|
export const FollowSupportedServices = [
|
||||||
|
EntityTypeEndpoint.DatabaseService,
|
||||||
|
EntityTypeEndpoint.DatabaseSchema,
|
||||||
|
EntityTypeEndpoint.Database,
|
||||||
|
];
|
||||||
|
|
||||||
export const VISIT_SERVICE_PAGE_DETAILS = {
|
export const VISIT_SERVICE_PAGE_DETAILS = {
|
||||||
[SERVICE_TYPE.Database]: {
|
[SERVICE_TYPE.Database]: {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
import { test } from '@playwright/test';
|
import { test } from '@playwright/test';
|
||||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||||
|
import { FollowSupportedServices } from '../../constant/service';
|
||||||
import { ApiCollectionClass } from '../../support/entity/ApiCollectionClass';
|
import { ApiCollectionClass } from '../../support/entity/ApiCollectionClass';
|
||||||
import { DatabaseClass } from '../../support/entity/DatabaseClass';
|
import { DatabaseClass } from '../../support/entity/DatabaseClass';
|
||||||
import { DatabaseSchemaClass } from '../../support/entity/DatabaseSchemaClass';
|
import { DatabaseSchemaClass } from '../../support/entity/DatabaseSchemaClass';
|
||||||
@ -158,6 +159,14 @@ entities.forEach((EntityClass) => {
|
|||||||
await afterAction();
|
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 }) => {
|
test(`Update displayName`, async ({ page }) => {
|
||||||
await entity.renameEntity(page, entity.entity.name);
|
await entity.renameEntity(page, entity.entity.name);
|
||||||
|
@ -144,16 +144,15 @@ export const DataAssetsHeader = ({
|
|||||||
) : null;
|
) : null;
|
||||||
}, [dataAsset]);
|
}, [dataAsset]);
|
||||||
|
|
||||||
const excludeEntityService = useMemo(
|
const excludeEntityService = useMemo(() => {
|
||||||
() =>
|
const filteredServiceTypes = SERVICE_TYPES.filter(
|
||||||
[
|
(type) => type !== EntityType.DATABASE_SERVICE
|
||||||
EntityType.DATABASE,
|
);
|
||||||
EntityType.DATABASE_SCHEMA,
|
|
||||||
EntityType.API_COLLECTION,
|
return [EntityType.API_COLLECTION, ...filteredServiceTypes].includes(
|
||||||
...SERVICE_TYPES,
|
entityType
|
||||||
].includes(entityType),
|
);
|
||||||
[entityType]
|
}, [entityType]);
|
||||||
);
|
|
||||||
|
|
||||||
const hasFollowers = 'followers' in dataAsset;
|
const hasFollowers = 'followers' in dataAsset;
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ describe('Test DatabaseDetails page', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(getDatabaseDetailsByFQN).toHaveBeenCalledWith('bigquery.shopify', {
|
expect(getDatabaseDetailsByFQN).toHaveBeenCalledWith('bigquery.shopify', {
|
||||||
fields: 'owners,tags,domain,votes,extension,dataProducts',
|
fields: 'owners,tags,domain,votes,extension,dataProducts,followers',
|
||||||
include: 'all',
|
include: 'all',
|
||||||
});
|
});
|
||||||
expect(entityHeader).toBeInTheDocument();
|
expect(entityHeader).toBeInTheDocument();
|
||||||
|
@ -37,6 +37,7 @@ import { EntityName } from '../../components/Modals/EntityNameModal/EntityNameMo
|
|||||||
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
|
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
|
||||||
import { ROUTES } from '../../constants/constants';
|
import { ROUTES } from '../../constants/constants';
|
||||||
import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants';
|
import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants';
|
||||||
|
import { GlobalSettingOptions } from '../../constants/GlobalSettings.constants';
|
||||||
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
@ -54,13 +55,16 @@ import { Database } from '../../generated/entity/data/database';
|
|||||||
import { PageType } from '../../generated/system/ui/uiCustomization';
|
import { PageType } from '../../generated/system/ui/uiCustomization';
|
||||||
import { Include } from '../../generated/type/include';
|
import { Include } from '../../generated/type/include';
|
||||||
import { useLocationSearch } from '../../hooks/LocationSearch/useLocationSearch';
|
import { useLocationSearch } from '../../hooks/LocationSearch/useLocationSearch';
|
||||||
|
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||||
import { useCustomPages } from '../../hooks/useCustomPages';
|
import { useCustomPages } from '../../hooks/useCustomPages';
|
||||||
import { useFqn } from '../../hooks/useFqn';
|
import { useFqn } from '../../hooks/useFqn';
|
||||||
import { FeedCounts } from '../../interface/feed.interface';
|
import { FeedCounts } from '../../interface/feed.interface';
|
||||||
import {
|
import {
|
||||||
|
addFollowers,
|
||||||
getDatabaseDetailsByFQN,
|
getDatabaseDetailsByFQN,
|
||||||
getDatabaseSchemas,
|
getDatabaseSchemas,
|
||||||
patchDatabaseDetails,
|
patchDatabaseDetails,
|
||||||
|
removeFollowers,
|
||||||
restoreDatabase,
|
restoreDatabase,
|
||||||
updateDatabaseVotes,
|
updateDatabaseVotes,
|
||||||
} from '../../rest/databaseAPI';
|
} from '../../rest/databaseAPI';
|
||||||
@ -111,11 +115,14 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
const isMounting = useRef(true);
|
const isMounting = useRef(true);
|
||||||
const [isTabExpanded, setIsTabExpanded] = useState(false);
|
const [isTabExpanded, setIsTabExpanded] = useState(false);
|
||||||
|
|
||||||
const { version: currentVersion, deleted } = useMemo(
|
const {
|
||||||
() => database,
|
version: currentVersion,
|
||||||
[database]
|
deleted,
|
||||||
);
|
id: databaseId,
|
||||||
|
} = useMemo(() => database, [database]);
|
||||||
|
|
||||||
|
const { currentUser } = useApplicationStore();
|
||||||
|
const USERId = currentUser?.id ?? '';
|
||||||
const tier = getTierTags(database?.tags ?? []);
|
const tier = getTierTags(database?.tags ?? []);
|
||||||
|
|
||||||
const [databasePermission, setDatabasePermission] =
|
const [databasePermission, setDatabasePermission] =
|
||||||
@ -177,7 +184,15 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
const getDetailsByFQN = () => {
|
const getDetailsByFQN = () => {
|
||||||
setIsDatabaseDetailsLoading(true);
|
setIsDatabaseDetailsLoading(true);
|
||||||
getDatabaseDetailsByFQN(decodedDatabaseFQN, {
|
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,
|
include: Include.All,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.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 () => {
|
const handleRestoreDatabase = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const { version: newVersion } = await restoreDatabase(database.id ?? '');
|
const { version: newVersion } = await restoreDatabase(database.id ?? '');
|
||||||
@ -431,6 +455,64 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
showErrorToast(error as AxiosError);
|
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 = () => {
|
const toggleTabExpanded = () => {
|
||||||
setIsTabExpanded(!isTabExpanded);
|
setIsTabExpanded(!isTabExpanded);
|
||||||
@ -471,6 +553,7 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
openTaskCount={feedCount.openTaskCount}
|
openTaskCount={feedCount.openTaskCount}
|
||||||
permissions={databasePermission}
|
permissions={databasePermission}
|
||||||
onDisplayNameUpdate={handleUpdateDisplayName}
|
onDisplayNameUpdate={handleUpdateDisplayName}
|
||||||
|
onFollowClick={handleFollowClick}
|
||||||
onOwnerUpdate={handleUpdateOwner}
|
onOwnerUpdate={handleUpdateOwner}
|
||||||
onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)}
|
onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)}
|
||||||
onRestoreDataAsset={handleRestoreDatabase}
|
onRestoreDataAsset={handleRestoreDatabase}
|
||||||
|
@ -40,6 +40,7 @@ import {
|
|||||||
ROUTES,
|
ROUTES,
|
||||||
} from '../../constants/constants';
|
} from '../../constants/constants';
|
||||||
import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants';
|
import { FEED_COUNT_INITIAL_DATA } from '../../constants/entity.constants';
|
||||||
|
import { GlobalSettingOptions } from '../../constants/GlobalSettings.constants';
|
||||||
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
@ -56,13 +57,16 @@ import { Tag } from '../../generated/entity/classification/tag';
|
|||||||
import { DatabaseSchema } from '../../generated/entity/data/databaseSchema';
|
import { DatabaseSchema } from '../../generated/entity/data/databaseSchema';
|
||||||
import { PageType } from '../../generated/system/ui/page';
|
import { PageType } from '../../generated/system/ui/page';
|
||||||
import { Include } from '../../generated/type/include';
|
import { Include } from '../../generated/type/include';
|
||||||
|
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||||
import { useCustomPages } from '../../hooks/useCustomPages';
|
import { useCustomPages } from '../../hooks/useCustomPages';
|
||||||
import { useFqn } from '../../hooks/useFqn';
|
import { useFqn } from '../../hooks/useFqn';
|
||||||
import { useTableFilters } from '../../hooks/useTableFilters';
|
import { useTableFilters } from '../../hooks/useTableFilters';
|
||||||
import { FeedCounts } from '../../interface/feed.interface';
|
import { FeedCounts } from '../../interface/feed.interface';
|
||||||
import {
|
import {
|
||||||
|
addFollowers,
|
||||||
getDatabaseSchemaDetailsByFQN,
|
getDatabaseSchemaDetailsByFQN,
|
||||||
patchDatabaseSchemaDetails,
|
patchDatabaseSchemaDetails,
|
||||||
|
removeFollowers,
|
||||||
restoreDatabaseSchema,
|
restoreDatabaseSchema,
|
||||||
updateDatabaseSchemaVotes,
|
updateDatabaseSchemaVotes,
|
||||||
} from '../../rest/databaseAPI';
|
} from '../../rest/databaseAPI';
|
||||||
@ -85,6 +89,8 @@ import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
|||||||
const DatabaseSchemaPage: FunctionComponent = () => {
|
const DatabaseSchemaPage: FunctionComponent = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getEntityPermissionByFqn } = usePermissionProvider();
|
const { getEntityPermissionByFqn } = usePermissionProvider();
|
||||||
|
const { currentUser } = useApplicationStore();
|
||||||
|
const USERId = currentUser?.id ?? '';
|
||||||
|
|
||||||
const { setFilters, filters } = useTableFilters(INITIAL_TABLE_FILTERS);
|
const { setFilters, filters } = useTableFilters(INITIAL_TABLE_FILTERS);
|
||||||
const { tab: activeTab = EntityTabs.TABLE } =
|
const { tab: activeTab = EntityTabs.TABLE } =
|
||||||
@ -111,6 +117,15 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
const [updateProfilerSetting, setUpdateProfilerSetting] =
|
const [updateProfilerSetting, setUpdateProfilerSetting] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
|
|
||||||
|
const { isFollowing, followers = [] } = useMemo(
|
||||||
|
() => ({
|
||||||
|
isFollowing: databaseSchema?.followers?.some(
|
||||||
|
({ id }) => id === currentUser?.id
|
||||||
|
),
|
||||||
|
followers: databaseSchema?.followers ?? [],
|
||||||
|
}),
|
||||||
|
[currentUser, databaseSchema]
|
||||||
|
);
|
||||||
const extraDropdownContent = useMemo(
|
const extraDropdownContent = useMemo(
|
||||||
() =>
|
() =>
|
||||||
entityUtilClassBase.getManageExtraOptions(
|
entityUtilClassBase.getManageExtraOptions(
|
||||||
@ -170,8 +185,16 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
const response = await getDatabaseSchemaDetailsByFQN(
|
const response = await getDatabaseSchemaDetailsByFQN(
|
||||||
decodedDatabaseSchemaFQN,
|
decodedDatabaseSchemaFQN,
|
||||||
{
|
{
|
||||||
// eslint-disable-next-line max-len
|
fields: [
|
||||||
fields: `${TabSpecificField.OWNERS},${TabSpecificField.USAGE_SUMMARY},${TabSpecificField.TAGS},${TabSpecificField.DOMAIN},${TabSpecificField.VOTES},${TabSpecificField.EXTENSION},${TabSpecificField.DATA_PRODUCTS}`,
|
TabSpecificField.OWNERS,
|
||||||
|
TabSpecificField.USAGE_SUMMARY,
|
||||||
|
TabSpecificField.TAGS,
|
||||||
|
TabSpecificField.DOMAIN,
|
||||||
|
TabSpecificField.VOTES,
|
||||||
|
TabSpecificField.EXTENSION,
|
||||||
|
TabSpecificField.FOLLOWERS,
|
||||||
|
TabSpecificField.DATA_PRODUCTS,
|
||||||
|
].join(','),
|
||||||
include: Include.All,
|
include: Include.All,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -500,6 +523,65 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
checkIfExpandViewSupported(tabs[0], activeTab, PageType.DatabaseSchema),
|
checkIfExpandViewSupported(tabs[0], activeTab, PageType.DatabaseSchema),
|
||||||
[tabs[0], activeTab]
|
[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) {
|
if (isPermissionsLoading) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
@ -542,6 +624,7 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
extraDropdownContent={extraDropdownContent}
|
extraDropdownContent={extraDropdownContent}
|
||||||
permissions={databaseSchemaPermission}
|
permissions={databaseSchemaPermission}
|
||||||
onDisplayNameUpdate={handleUpdateDisplayName}
|
onDisplayNameUpdate={handleUpdateDisplayName}
|
||||||
|
onFollowClick={handleFollowClick}
|
||||||
onOwnerUpdate={handleUpdateOwner}
|
onOwnerUpdate={handleUpdateOwner}
|
||||||
onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)}
|
onProfilerSettingUpdate={() => setUpdateProfilerSetting(true)}
|
||||||
onRestoreDataAsset={handleRestoreDatabaseSchema}
|
onRestoreDataAsset={handleRestoreDatabaseSchema}
|
||||||
|
@ -198,6 +198,7 @@ const API_FIELDS = [
|
|||||||
'domain',
|
'domain',
|
||||||
'votes',
|
'votes',
|
||||||
'extension',
|
'extension',
|
||||||
|
'followers',
|
||||||
'dataProducts',
|
'dataProducts',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -94,8 +94,10 @@ import { getPipelines } from '../../rest/pipelineAPI';
|
|||||||
import { searchQuery } from '../../rest/searchAPI';
|
import { searchQuery } from '../../rest/searchAPI';
|
||||||
import { getSearchIndexes } from '../../rest/SearchIndexAPI';
|
import { getSearchIndexes } from '../../rest/SearchIndexAPI';
|
||||||
import {
|
import {
|
||||||
|
addServiceFollower,
|
||||||
getServiceByFQN,
|
getServiceByFQN,
|
||||||
patchService,
|
patchService,
|
||||||
|
removeServiceFollower,
|
||||||
restoreService,
|
restoreService,
|
||||||
} from '../../rest/serviceAPI';
|
} from '../../rest/serviceAPI';
|
||||||
import { getContainers } from '../../rest/storageAPI';
|
import { getContainers } from '../../rest/storageAPI';
|
||||||
@ -173,7 +175,7 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
const [workflowStatesData, setWorkflowStatesData] =
|
const [workflowStatesData, setWorkflowStatesData] =
|
||||||
useState<WorkflowStatesData>();
|
useState<WorkflowStatesData>();
|
||||||
const [isWorkflowStatusLoading, setIsWorkflowStatusLoading] = useState(true);
|
const [isWorkflowStatusLoading, setIsWorkflowStatusLoading] = useState(true);
|
||||||
|
const USERId = currentUser?.id ?? '';
|
||||||
const {
|
const {
|
||||||
paging: collateAgentPaging,
|
paging: collateAgentPaging,
|
||||||
pageSize: collateAgentPageSize,
|
pageSize: collateAgentPageSize,
|
||||||
@ -225,6 +227,15 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
const [isCollateAgentLoading, setIsCollateAgentLoading] = useState(false);
|
const [isCollateAgentLoading, setIsCollateAgentLoading] = useState(false);
|
||||||
const [collateAgentsList, setCollateAgentsList] = useState<App[]>([]);
|
const [collateAgentsList, setCollateAgentsList] = useState<App[]>([]);
|
||||||
|
|
||||||
|
const { isFollowing, followers = [] } = useMemo(
|
||||||
|
() => ({
|
||||||
|
isFollowing: serviceDetails?.followers?.some(
|
||||||
|
({ id }) => id === currentUser?.id
|
||||||
|
),
|
||||||
|
followers: serviceDetails?.followers ?? [],
|
||||||
|
}),
|
||||||
|
[serviceDetails, currentUser]
|
||||||
|
);
|
||||||
const { CollateAIAgentsWidget } = useMemo(
|
const { CollateAIAgentsWidget } = useMemo(
|
||||||
() => serviceUtilClassBase.getAgentsTabWidgets(),
|
() => serviceUtilClassBase.getAgentsTabWidgets(),
|
||||||
[]
|
[]
|
||||||
@ -301,10 +312,11 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
return shouldTestConnection(serviceCategory);
|
return shouldTestConnection(serviceCategory);
|
||||||
}, [serviceCategory]);
|
}, [serviceCategory]);
|
||||||
|
|
||||||
const { version: currentVersion, deleted } = useMemo(
|
const {
|
||||||
() => serviceDetails,
|
version: currentVersion,
|
||||||
[serviceDetails]
|
deleted,
|
||||||
);
|
id: serviceId,
|
||||||
|
} = useMemo(() => serviceDetails, [serviceDetails]);
|
||||||
|
|
||||||
const fetchServicePermission = useCallback(async () => {
|
const fetchServicePermission = useCallback(async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -719,8 +731,10 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
decodedServiceFQN,
|
decodedServiceFQN,
|
||||||
{
|
{
|
||||||
fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${
|
fields: `${TabSpecificField.OWNERS},${TabSpecificField.TAGS},${
|
||||||
isMetadataService ? '' : TabSpecificField.DATA_PRODUCTS
|
TabSpecificField.FOLLOWERS
|
||||||
},${isMetadataService ? '' : TabSpecificField.DOMAIN}`,
|
},${isMetadataService ? '' : TabSpecificField.DATA_PRODUCTS},${
|
||||||
|
isMetadataService ? '' : TabSpecificField.DOMAIN
|
||||||
|
}`,
|
||||||
include: Include.All,
|
include: Include.All,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -738,6 +752,55 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
}
|
}
|
||||||
}, [serviceCategory, decodedServiceFQN, isMetadataService]);
|
}, [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(
|
const handleUpdateDisplayName = useCallback(
|
||||||
async (data: EntityName) => {
|
async (data: EntityName) => {
|
||||||
if (isEmpty(serviceDetails)) {
|
if (isEmpty(serviceDetails)) {
|
||||||
@ -1427,6 +1490,7 @@ const ServiceDetailsPage: FunctionComponent = () => {
|
|||||||
permissions={servicePermission}
|
permissions={servicePermission}
|
||||||
showDomain={!isMetadataService}
|
showDomain={!isMetadataService}
|
||||||
onDisplayNameUpdate={handleUpdateDisplayName}
|
onDisplayNameUpdate={handleUpdateDisplayName}
|
||||||
|
onFollowClick={handleFollowClick}
|
||||||
onOwnerUpdate={handleUpdateOwner}
|
onOwnerUpdate={handleUpdateOwner}
|
||||||
onRestoreDataAsset={handleRestoreService}
|
onRestoreDataAsset={handleRestoreService}
|
||||||
onTierUpdate={handleUpdateTier}
|
onTierUpdate={handleUpdateTier}
|
||||||
|
@ -15,11 +15,13 @@ import { AxiosResponse } from 'axios';
|
|||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import { PagingWithoutTotal, RestoreRequestType } from 'Models';
|
import { PagingWithoutTotal, RestoreRequestType } from 'Models';
|
||||||
import { QueryVote } from '../components/Database/TableQueries/TableQueries.interface';
|
import { QueryVote } from '../components/Database/TableQueries/TableQueries.interface';
|
||||||
|
import { APPLICATION_JSON_CONTENT_TYPE_HEADER } from '../constants/constants';
|
||||||
import {
|
import {
|
||||||
Database,
|
Database,
|
||||||
DatabaseProfilerConfig as ProfilerConfig,
|
DatabaseProfilerConfig as ProfilerConfig,
|
||||||
} from '../generated/entity/data/database';
|
} from '../generated/entity/data/database';
|
||||||
import { DatabaseSchema } from '../generated/entity/data/databaseSchema';
|
import { DatabaseSchema } from '../generated/entity/data/databaseSchema';
|
||||||
|
import { EntityReference } from '../generated/entity/type';
|
||||||
import { EntityHistory } from '../generated/type/entityHistory';
|
import { EntityHistory } from '../generated/type/entityHistory';
|
||||||
import { Include } from '../generated/type/include';
|
import { Include } from '../generated/type/include';
|
||||||
import { Paging } from '../generated/type/paging';
|
import { Paging } from '../generated/type/paging';
|
||||||
@ -86,6 +88,35 @@ export const patchDatabaseSchemaDetails = async (
|
|||||||
return response.data;
|
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 ({
|
export const getDatabaseSchemas = async ({
|
||||||
include = Include.NonDeleted,
|
include = Include.NonDeleted,
|
||||||
databaseName,
|
databaseName,
|
||||||
|
@ -14,9 +14,13 @@
|
|||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { RestoreRequestType, ServicesUpdateRequest } from 'Models';
|
import { RestoreRequestType, ServicesUpdateRequest } from 'Models';
|
||||||
import { WILD_CARD_CHAR } from '../constants/char.constants';
|
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 { TabSpecificField } from '../enums/entity.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
|
import { EntityReference } from '../generated/entity/type';
|
||||||
import { EntityHistory } from '../generated/type/entityHistory';
|
import { EntityHistory } from '../generated/type/entityHistory';
|
||||||
import { Include } from '../generated/type/include';
|
import { Include } from '../generated/type/include';
|
||||||
import { ListParams } from '../interface/API.interface';
|
import { ListParams } from '../interface/API.interface';
|
||||||
@ -97,6 +101,34 @@ export const postService = async (
|
|||||||
return response.data;
|
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 (
|
export const patchService = async (
|
||||||
serviceCat: string,
|
serviceCat: string,
|
||||||
id: string,
|
id: string,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user