UI(issue#6234) : Manage tab removal (#6536)

* Removed manage tabs from Table, Topic, Dashboard, Pipeline and ML Model entities
Modified unit tests according to the changes made in comnponents

* Fixed error while deleting entities except tables and also added test-ids to edit icons for cypress tests

* * Added functionaliy to delete entities through ManageButton on Service, DatabaseSchema and DatbaseDetails page.

* Added functionality to edit the owner through entity summary details on DatabaseSchema and DatabaseDetails page.
* Removed manage tab from Service, DatabaseSchema and DatabaseDetails page and did relevant utils changes.

* Cypress test fixes for manage tab removal and bug fix on service page

* * Service deletion message added

* Removed the option for soft delete for service, database and database schemas

* Cypress test slectors changed for specificity

* Added mocks for all APIs in service page unit tests

* Fixed failing unit tests

* Removed unused props

* Resolved onAnnouncementClick handler error.
This commit is contained in:
Aniket Katkar 2022-08-06 12:38:27 +05:30 committed by GitHub
parent 1182402872
commit af0079683c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 474 additions and 737 deletions

View File

@ -15,6 +15,8 @@
export const uuid = () => Cypress._.random(0, 1e6); export const uuid = () => Cypress._.random(0, 1e6);
const CLOUD_INFRA = 'Cloud_Infra';
const isDatabaseService = (type) => type === 'database'; const isDatabaseService = (type) => type === 'database';
export const handleIngestionRetry = (type, testIngestionButton, count = 0) => { export const handleIngestionRetry = (type, testIngestionButton, count = 0) => {
@ -135,7 +137,9 @@ export const testServiceCreationAndIngestion = (
cy.get('[data-testid="deploy-button"]').should('be.visible').click(); cy.get('[data-testid="deploy-button"]').should('be.visible').click();
// check success // check success
cy.get('[data-testid="success-line"]', { timeout: 15000 }).should('be.visible'); cy.get('[data-testid="success-line"]', { timeout: 15000 }).should(
'be.visible'
);
cy.contains(`"${serviceName}_metadata"`).should('be.visible'); cy.contains(`"${serviceName}_metadata"`).should('be.visible');
cy.contains('has been created and deployed successfully').should( cy.contains('has been created and deployed successfully').should(
'be.visible' 'be.visible'
@ -158,12 +162,7 @@ export const testServiceCreationAndIngestion = (
export const deleteCreatedService = (typeOfService, service_Name) => { export const deleteCreatedService = (typeOfService, service_Name) => {
cy.goToHomePage(); cy.goToHomePage();
cy.get( cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click();
'.tw-ml-5 > [data-testid="dropdown-item"] > div > [data-testid="menu-button"]'
)
.scrollIntoView()
.should('be.visible')
.click();
cy.get('[data-testid="menu-item-Services"]').should('be.visible').click(); cy.get('[data-testid="menu-item-Services"]').should('be.visible').click();
cy.wait(1000); cy.wait(1000);
@ -186,15 +185,26 @@ export const deleteCreatedService = (typeOfService, service_Name) => {
cy.wait(1000); cy.wait(1000);
cy.get('[data-testid="Manage"]').should('exist').should('be.visible').click(); cy.get('[data-testid="manage-button"]')
cy.get('[data-testid="delete-button"]')
.scrollIntoView()
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('[data-testid="delete-button"] > .tw-font-medium')
.should('exist')
.should('be.visible')
.click();
//Clicking on permanent delete radio button and checking the service name
cy.get('[data-testid="hard-delete-option"]')
.contains(service_Name)
.should('be.visible')
.click();
cy.get('[data-testid="confirmation-text-input"]') cy.get('[data-testid="confirmation-text-input"]')
.should('be.visible') .should('be.visible')
.type('DELETE'); .type('DELETE');
cy.get('[data-testid="confirm-button"]').should('be.visible').click(); cy.get('[data-testid="confirm-button"]').should('be.visible').click();
cy.wait(2000); cy.wait(2000);
cy.get('[class="Toastify__toast-body"] >div') cy.get('[class="Toastify__toast-body"] >div')
@ -202,6 +212,65 @@ export const deleteCreatedService = (typeOfService, service_Name) => {
.should('exist') .should('exist')
.should('be.visible') .should('be.visible')
.should('have.text', `${typeOfService} Service deleted successfully!`); .should('have.text', `${typeOfService} Service deleted successfully!`);
//Checking if the service got deleted successfully
cy.clickOnLogo();
cy.wait(1000);
cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click();
cy.get('[data-testid="menu-item-Services"]').should('be.visible').click();
cy.wait(1000);
//redirecting to services page
cy.contains('[data-testid="tab"]', `${typeOfService} Service`).click();
cy.get(`[data-testid="service-name-${service_Name}"]`).should('not.exist');
};
export const editOwnerforCreatedService = (typeOfService, service_Name) => {
cy.goToHomePage();
cy.get('#menu-button-Settings').scrollIntoView().should('be.visible').click();
cy.get('[data-testid="menu-item-Services"]').should('be.visible').click();
cy.wait(1000);
//redirecting to services page
cy.contains('[data-testid="tab"]', `${typeOfService} Service`).click();
//click on created service
cy.get(`[data-testid="service-name-${service_Name}"]`)
.should('exist')
.should('be.visible')
.click();
cy.wait(1000);
//Click on edit owner button
cy.get('[data-testid="edit-Owner-icon"]')
.should('exist')
.should('be.visible')
.click();
cy.wait(500);
//Clicking on users tab
cy.get('[data-testid="dropdown-tab"]')
.contains('Teams')
.should('exist')
.should('be.visible')
.click();
//Selecting the user
cy.get('[data-testid="list-item"]')
.should('exist')
.should('be.visible')
.click();
cy.wait(1000);
cy.get('[data-testid*="owner"]')
.invoke('text')
.then((text) => {
expect(text).equal(CLOUD_INFRA);
});
}; };
export const goToAddNewServicePage = () => { export const goToAddNewServicePage = () => {

View File

@ -11,8 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'BigQuery'; const serviceType = 'BigQuery';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -68,6 +67,10 @@ describe('BigQuery Ingestion', () => {
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Database', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Database', serviceName); deleteCreatedService('Database', serviceName);
}); });

View File

@ -11,8 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Glue'; const serviceType = 'Glue';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -52,6 +51,10 @@ describe('Glue Ingestion', () => {
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Database', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Database', serviceName); deleteCreatedService('Database', serviceName);
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Kafka'; const serviceType = 'Kafka';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -45,6 +45,10 @@ describe('Kafka Ingestion', () => {
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Messaging', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Messaging', serviceName); deleteCreatedService('Messaging', serviceName);
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Metabase'; const serviceType = 'Metabase';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -46,6 +46,10 @@ describe('Metabase Ingestion', () => {
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Dashboard', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Dashboard', serviceName); deleteCreatedService('Dashboard', serviceName);
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Mysql'; const serviceType = 'Mysql';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -37,11 +37,15 @@ describe('MySQL Ingestion', () => {
serviceType, serviceType,
connectionInput, connectionInput,
addIngestionInput, addIngestionInput,
serviceName, serviceName
); );
}); });
it('delete created service', () => { it('Edit and validate owner', () => {
deleteCreatedService('Database', serviceName); editOwnerforCreatedService('Database', serviceName);
}); });
it('delete created service', () => {
deleteCreatedService('Database', serviceName);
});
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Redshift'; const serviceType = 'Redshift';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -44,10 +44,14 @@ describe('RedShift Ingestion', () => {
serviceType, serviceType,
connectionInput, connectionInput,
addIngestionInput, addIngestionInput,
serviceName, serviceName
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Database', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Database', serviceName); deleteCreatedService('Database', serviceName);
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Snowflake'; const serviceType = 'Snowflake';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -38,10 +38,14 @@ describe('Snowflake Ingestion', () => {
serviceType, serviceType,
connectionInput, connectionInput,
addIngestionInput, addIngestionInput,
serviceName, serviceName
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Database', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Database', serviceName); deleteCreatedService('Database', serviceName);
}); });

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { deleteCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common'; import { deleteCreatedService, editOwnerforCreatedService, goToAddNewServicePage, testServiceCreationAndIngestion, uuid } from '../../common/common';
const serviceType = 'Superset'; const serviceType = 'Superset';
const serviceName = `${serviceType}-ct-test-${uuid()}`; const serviceName = `${serviceType}-ct-test-${uuid()}`;
@ -48,6 +48,10 @@ describe('Superset Ingestion', () => {
); );
}); });
it('Edit and validate owner', () => {
editOwnerforCreatedService('Dashboard', serviceName);
});
it('delete created service', () => { it('delete created service', () => {
deleteCreatedService('Dashboard', serviceName); deleteCreatedService('Dashboard', serviceName);
}); });

View File

@ -29,27 +29,43 @@ describe('Entity Details Page', () => {
.should('have.class', 'active') .should('have.class', 'active')
.click(); .click();
// click on the 1st result and go to manage tab in entity details page
cy.wait(500); cy.wait(500);
cy.get('[data-testid="table-link"]').first().should('be.visible').click();
cy.get('[data-testid="Manage"]').click();
//Click on manage button
cy.get('[data-testid="table-link"]').first().should('be.visible').click();
cy.get('[data-testid="manage-button"]')
.should('exist')
.should('be.visible')
.click();
cy.wait(1000);
// check for delete section and delete button is available or not // check for delete section and delete button is available or not
cy.get('[data-testid="danger-zone"]').scrollIntoView().should('be.visible'); // cy.get('[data-testid="danger-zone"]').scrollIntoView().should('be.visible');
cy.get('[data-testid="hard-delete"] > .tw-flex') cy.get('[data-testid="delete-button-title"]')
.scrollIntoView()
.find('[data-testid="delete-button"]')
.should('be.visible') .should('be.visible')
.click() .click()
.as('deleteBtn'); .as('deleteBtn');
cy.wait(1000);
cy.get('[data-testid="hard-delete-option"]')
.should('contain', `Permanently Delete ${singuler}${value.term}`)
.should('be.visible')
.as('permanentDelete');
cy.get('@permanentDelete').should('be.visible').click();
cy.get('[data-testid="confirm-button"]') cy.get('[data-testid="confirm-button"]')
.should('be.visible') .should('be.visible')
.as('confirmBtn'); .as('confirmBtn');
cy.get('@confirmBtn').should('be.disabled'); cy.get('@confirmBtn').should('be.disabled');
cy.get('[data-testid="discard-button"]') cy.get('[data-testid="discard-button"]')
.should('be.visible') .should('be.visible')
.as('discardBtn'); .as('discardBtn');
cy.get('[data-testid="confirmation-text-input"]') cy.get('[data-testid="confirmation-text-input"]')
.should('be.visible') .should('be.visible')
.as('textBox'); .as('textBox');
@ -57,8 +73,16 @@ describe('Entity Details Page', () => {
// delete modal should be disappeared // delete modal should be disappeared
cy.get('@discardBtn').click(); cy.get('@discardBtn').click();
cy.get('[data-testid="manage-button"]')
.should('exist')
.should('be.visible')
.click();
// open modal and type required text in input box to delete entity // open modal and type required text in input box to delete entity
cy.get('@deleteBtn').click(); cy.get('@deleteBtn').click();
cy.wait(1000);
cy.get('@permanentDelete').click();
cy.get('@textBox').type(DELETE_TERM); cy.get('@textBox').type(DELETE_TERM);
cy.get('@confirmBtn').should('not.be.disabled'); cy.get('@confirmBtn').should('not.be.disabled');
cy.get('@confirmBtn').click(); cy.get('@confirmBtn').click();
@ -96,6 +120,87 @@ describe('Entity Details Page', () => {
cy.clickOnLogo(); cy.clickOnLogo();
}; };
const addOwnerAndTier = (value) => {
searchEntity(value.term);
cy.get(`[data-testid="${value.entity}-tab"]`).should('be.visible').click();
cy.get(`[data-testid="${value.entity}-tab"]`)
.should('be.visible')
.should('have.class', 'active')
.click();
cy.get('[data-testid="table-link"]').first().should('be.visible').click();
cy.wait(500);
cy.get('[data-testid="edit-Owner-icon"]').should('be.visible').click();
cy.wait(500);
//Clicking on users tab
cy.get('[data-testid="dropdown-tab"]')
.contains('Users')
.should('exist')
.should('be.visible')
.click();
//Selecting the user
cy.get('[data-testid="list-item"]')
.should('exist')
.should('be.visible')
.click();
cy.wait(1000);
cy.get('[data-testid="owner-dropdown"]')
.invoke('text')
.then((text) => {
expect(text).equal('Aaron Johnson');
});
cy.wait(1000);
cy.get('[data-testid="edit-Tier-icon"]')
.scrollIntoView()
.should('exist')
.should('be.visible')
.click();
cy.get('[data-testid="select-tier-buuton"]')
.first()
.should('exist')
.should('be.visible')
.click();
cy.get('[data-testid="tier-dropdown"]')
.invoke('text')
.then((text) => {
expect(text).equal('Tier1');
});
cy.get('[data-testid="entity-tags"]').should('contain', 'Tier1');
cy.wait(1000);
cy.clickOnLogo();
// checks newly generated feed for follow and setting owner
cy.get('[data-testid="message-container"]')
.eq(1)
.contains('Added owner: Aaron Johnson')
.should('be.visible');
cy.get('[data-testid="message-container"]')
.eq(0)
.scrollIntoView()
.contains('Added tags: Tier.Tier1')
.should('be.visible');
cy.clickOnLogo();
};
it('Add Owner and Tier for entity', () => {
Object.values(DELETE_ENTITY).forEach((value) => {
addOwnerAndTier(value);
});
});
it('Delete entity flow should work properly', () => { it('Delete entity flow should work properly', () => {
Object.values(DELETE_ENTITY).forEach((value) => { Object.values(DELETE_ENTITY).forEach((value) => {
deleteEntity(value); deleteEntity(value);

View File

@ -92,43 +92,45 @@ describe('MyData page should work', () => {
cy.get('[data-testid="follow-button"]').should('be.visible').click(); cy.get('[data-testid="follow-button"]').should('be.visible').click();
// go to manage tab and search for logged in user and set the owner // go to manage tab and search for logged in user and set the owner
cy.get('[data-testid="Manage"]').should('be.visible').click();
cy.get( cy.get('[data-testid="edit-Owner-icon"]').should('be.visible').click();
'[data-testid="dropdown-profile"] > [data-testid="dropdown-item"] > :nth-child(1) > [data-testid="menu-button"]'
) cy.wait(500);
//Clicking on users tab
cy.get('[data-testid="dropdown-tab"]')
.contains('Users')
.should('exist')
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('[data-testid="greeting-text"] > a > :nth-child(1)')
//Selecting the user
cy.get('[data-testid="list-item"]')
.should('exist')
.should('be.visible') .should('be.visible')
.click();
cy.wait(1000);
cy.get('[data-testid="owner-dropdown"]')
.invoke('text') .invoke('text')
.then((name) => { .then((text) => {
cy.get('.tw-z-10').click(); expect(text).equal('Aaron Johnson');
cy.get('[data-testid="owner-dropdown"]').should('be.visible').click();
cy.get('[data-testid="dropdown-tab"]').eq(1).should('exist').click();
cy.get('[data-testid="list-item"]').should('be.visible').click();
cy.wait(500);
cy.get('[data-testid="owner-dropdown"] > .tw-truncate')
.invoke('text')
.then((text) => {
expect(text).equal(name);
});
cy.clickOnLogo();
// checks newly generated feed for follow and setting owner
cy.get('[data-testid="message-container"]')
.first()
.contains(`Added owner: ${name}`)
.should('be.visible');
cy.get('[data-testid="message-container"]')
.eq(1)
.scrollIntoView()
.contains(`Followed ${termObj.entity.slice(0, -1)}`)
.should('be.visible');
}); });
cy.clickOnLogo(); cy.clickOnLogo();
// checks newly generated feed for follow and setting owner
cy.get('[data-testid="message-container"]')
.first()
.contains('Added owner: Aaron Johnson')
.should('be.visible');
cy.get('[data-testid="message-container"]')
.eq(1)
.scrollIntoView()
.contains(`Followed ${termObj.entity.slice(0, -1)}`)
.should('be.visible');
cy.clickOnLogo();
}; };
it('MyData Page should render properly with all the required components', () => { it('MyData Page should render properly with all the required components', () => {

View File

@ -34,12 +34,10 @@ import { LabelType, State, TagLabel } from '../../generated/type/tagLabel';
import { useInfiniteScroll } from '../../hooks/useInfiniteScroll'; import { useInfiniteScroll } from '../../hooks/useInfiniteScroll';
import { import {
getCurrentUserId, getCurrentUserId,
getEntityDeleteMessage,
getEntityName, getEntityName,
getEntityPlaceHolder, getEntityPlaceHolder,
getHtmlForNonAdminAction, getHtmlForNonAdminAction,
isEven, isEven,
pluralize,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { getEntityFeedLink } from '../../utils/EntityUtils'; import { getEntityFeedLink } from '../../utils/EntityUtils';
import { getDefaultValue } from '../../utils/FeedElementUtils'; import { getDefaultValue } from '../../utils/FeedElementUtils';
@ -63,7 +61,6 @@ import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer'; import PageContainer from '../containers/PageContainer';
import Entitylineage from '../EntityLineage/EntityLineage.component'; import Entitylineage from '../EntityLineage/EntityLineage.component';
import Loader from '../Loader/Loader'; import Loader from '../Loader/Loader';
import ManageTabComponent from '../ManageTab/ManageTab.component';
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal';
import TagsContainer from '../tags-container/tags-container'; import TagsContainer from '../tags-container/tags-container';
@ -200,18 +197,6 @@ const DashboardDetails = ({
isProtected: false, isProtected: false,
position: 4, position: 4,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 5,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
@ -292,34 +277,6 @@ const DashboardDetails = ({
settingsUpdateHandler(updatedDashboardDetails); settingsUpdateHandler(updatedDashboardDetails);
} }
}; };
const onSettingsUpdate = (
newOwner?: Dashboard['owner'],
newTier?: string
) => {
if (newOwner || newTier) {
const tierTag: Dashboard['tags'] = newTier
? [
...getTagsWithoutTier(dashboardDetails.tags as Array<EntityTags>),
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: dashboardDetails.tags;
const updatedDashboardDetails = {
...dashboardDetails,
owner: newOwner
? { ...dashboardDetails.owner, ...newOwner }
: dashboardDetails.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedDashboardDetails);
} else {
return Promise.reject();
}
};
const onTagUpdate = (selectedTags?: Array<EntityTags>) => { const onTagUpdate = (selectedTags?: Array<EntityTags>) => {
if (selectedTags) { if (selectedTags) {
@ -458,17 +415,6 @@ const DashboardDetails = ({
setThreadLink(''); setThreadLink('');
}; };
const getDeleteEntityMessage = () => {
if (!charts.length) {
return;
}
return getEntityDeleteMessage(
EntityType.DASHBOARD,
pluralize(charts.length, 'Chart')
);
};
const getLoader = () => { const getLoader = () => {
return isentityThreadLoading ? <Loader /> : null; return isentityThreadLoading ? <Loader /> : null;
}; };
@ -513,6 +459,7 @@ const DashboardDetails = ({
entityFieldThreadCount entityFieldThreadCount
)} )}
entityFqn={dashboardFQN} entityFqn={dashboardFQN}
entityId={dashboardDetails.id}
entityName={entityName} entityName={entityName}
entityType={EntityType.DASHBOARD} entityType={EntityType.DASHBOARD}
extraInfo={extraInfo} extraInfo={extraInfo}
@ -755,25 +702,6 @@ const DashboardDetails = ({
handleExtentionUpdate={onExtensionUpdate} handleExtentionUpdate={onExtensionUpdate}
/> />
)} )}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete
allowSoftDelete={!deleted}
currentTier={tier?.tagFQN}
currentUser={owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={dashboardDetails.id}
entityName={dashboardDetails.name}
entityType={EntityType.DASHBOARD}
hasEditAccess={hasEditAccess()}
hideOwner={deleted}
hideTier={deleted}
manageSectionType={EntityType.DASHBOARD}
onSave={onSettingsUpdate}
/>
</div>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -181,10 +181,6 @@ window.IntersectionObserver = jest.fn().mockImplementation(() => ({
unobserve: mockunObserve, unobserve: mockunObserve,
})); }));
jest.mock('../ManageTab/ManageTab.component', () => {
return jest.fn().mockReturnValue(<p data-testid="manage">ManageTab</p>);
});
jest.mock('../common/description/Description', () => { jest.mock('../common/description/Description', () => {
return jest.fn().mockReturnValue(<p>Description Component</p>); return jest.fn().mockReturnValue(<p>Description Component</p>);
}); });
@ -288,7 +284,6 @@ describe('Test DashboardDetails component', () => {
const detailsTab = await findByTestId(tabs, 'Details'); const detailsTab = await findByTestId(tabs, 'Details');
const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks'); const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks');
const lineageTab = await findByTestId(tabs, 'Lineage'); const lineageTab = await findByTestId(tabs, 'Lineage');
const manageTab = await findByTestId(tabs, 'Manage');
expect(EntityPageInfo).toBeInTheDocument(); expect(EntityPageInfo).toBeInTheDocument();
expect(description).toBeInTheDocument(); expect(description).toBeInTheDocument();
@ -296,7 +291,6 @@ describe('Test DashboardDetails component', () => {
expect(detailsTab).toBeInTheDocument(); expect(detailsTab).toBeInTheDocument();
expect(activityFeedTab).toBeInTheDocument(); expect(activityFeedTab).toBeInTheDocument();
expect(lineageTab).toBeInTheDocument(); expect(lineageTab).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
}); });
it('Check if active tab is details', async () => { it('Check if active tab is details', async () => {
@ -335,18 +329,6 @@ describe('Test DashboardDetails component', () => {
expect(lineage).toBeInTheDocument(); expect(lineage).toBeInTheDocument();
}); });
it('Check if active tab is manage', async () => {
const { container } = render(
<DashboardDetails {...DashboardDetailsProps} activeTab={5} />,
{
wrapper: MemoryRouter,
}
);
const manage = await findByTestId(container, 'manage');
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => { it('Check if active tab is custom properties', async () => {
const { container } = render( const { container } = render(
<DashboardDetails {...DashboardDetailsProps} activeTab={4} />, <DashboardDetails {...DashboardDetailsProps} activeTab={4} />,

View File

@ -67,7 +67,6 @@ import DataQualityTab from '../DataQualityTab/DataQualityTab';
import Entitylineage from '../EntityLineage/EntityLineage.component'; import Entitylineage from '../EntityLineage/EntityLineage.component';
import FrequentlyJoinedTables from '../FrequentlyJoinedTables/FrequentlyJoinedTables.component'; import FrequentlyJoinedTables from '../FrequentlyJoinedTables/FrequentlyJoinedTables.component';
import Loader from '../Loader/Loader'; import Loader from '../Loader/Loader';
import ManageTab from '../ManageTab/ManageTab.component';
import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal';
import SampleDataTable, { import SampleDataTable, {
SampleColumns, SampleColumns,
@ -299,18 +298,6 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
isProtected: false, isProtected: false,
position: 9, position: 9,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: false,
protectedState: !owner || hasEditAccess(),
position: 10,
},
]; ];
const getFrequentlyJoinedWithTables = (): Array< const getFrequentlyJoinedWithTables = (): Array<
@ -487,35 +474,6 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
} }
}; };
const onSettingsUpdate = (newOwner?: Table['owner'], newTier?: string) => {
if (newOwner || newTier) {
const tierTag: Table['tags'] = newTier
? [
...getTagsWithoutTier(tableDetails.tags as Array<EntityTags>),
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: tableDetails.tags;
const updatedTableDetails = {
...tableDetails,
owner: newOwner
? {
...tableDetails.owner,
...newOwner,
}
: tableDetails.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedTableDetails);
} else {
return Promise.reject();
}
};
/** /**
* Formulates updated tags and updates table entity data for API call * Formulates updated tags and updates table entity data for API call
* @param selectedTags * @param selectedTags
@ -844,24 +802,6 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
handleExtentionUpdate={handleExtentionUpdate} handleExtentionUpdate={handleExtentionUpdate}
/> />
)} )}
{activeTab === 10 && (
<div>
<ManageTab
allowDelete
allowSoftDelete={!deleted}
currentTier={tier?.tagFQN}
currentUser={owner}
entityId={tableDetails.id}
entityName={tableDetails.name}
entityType={EntityType.TABLE}
hasEditAccess={hasEditAccess()}
hideOwner={deleted}
hideTier={deleted}
manageSectionType={EntityType.TABLE}
onSave={onSettingsUpdate}
/>
</div>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -166,9 +166,6 @@ const DatasetDetailsProps = {
handleExtentionUpdate: jest.fn(), handleExtentionUpdate: jest.fn(),
updateThreadHandler: jest.fn(), updateThreadHandler: jest.fn(),
}; };
jest.mock('../ManageTab/ManageTab.component', () => {
return jest.fn().mockReturnValue(<p data-testid="manage">ManageTab</p>);
});
jest.mock('../EntityLineage/EntityLineage.component', () => { jest.mock('../EntityLineage/EntityLineage.component', () => {
return jest.fn().mockReturnValue(<p data-testid="lineage">Lineage</p>); return jest.fn().mockReturnValue(<p data-testid="lineage">Lineage</p>);
@ -239,7 +236,6 @@ describe('Test MyDataDetailsPage page', () => {
const profilerTab = await findByTestId(tabs, 'Profiler'); const profilerTab = await findByTestId(tabs, 'Profiler');
const dataQualityTab = await findByTestId(tabs, 'Data Quality'); const dataQualityTab = await findByTestId(tabs, 'Data Quality');
const lineageTab = await findByTestId(tabs, 'Lineage'); const lineageTab = await findByTestId(tabs, 'Lineage');
const manageTab = await findByTestId(tabs, 'Manage');
const dbtTab = queryByTestId(tabs, 'DBT'); const dbtTab = queryByTestId(tabs, 'DBT');
expect(relatedTables).toBeInTheDocument(); expect(relatedTables).toBeInTheDocument();
@ -253,7 +249,6 @@ describe('Test MyDataDetailsPage page', () => {
expect(profilerTab).toBeInTheDocument(); expect(profilerTab).toBeInTheDocument();
expect(dataQualityTab).toBeInTheDocument(); expect(dataQualityTab).toBeInTheDocument();
expect(lineageTab).toBeInTheDocument(); expect(lineageTab).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
expect(dbtTab).not.toBeInTheDocument(); expect(dbtTab).not.toBeInTheDocument();
}); });
@ -338,18 +333,6 @@ describe('Test MyDataDetailsPage page', () => {
expect(lineage).toBeInTheDocument(); expect(lineage).toBeInTheDocument();
}); });
it('Check if active tab is manage', async () => {
const { container } = render(
<DatasetDetails {...DatasetDetailsProps} activeTab={10} />,
{
wrapper: MemoryRouter,
}
);
const manage = await findByTestId(container, 'manage');
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => { it('Check if active tab is custom properties', async () => {
const { container } = render( const { container } = render(
<DatasetDetails {...DatasetDetailsProps} activeTab={9} />, <DatasetDetails {...DatasetDetailsProps} activeTab={9} />,

View File

@ -155,10 +155,6 @@ const mockProp = {
onExtensionUpdate: jest.fn(), onExtensionUpdate: jest.fn(),
}; };
jest.mock('../ManageTab/ManageTab.component', () => {
return jest.fn().mockReturnValue(<p data-testid="manage">ManageTab</p>);
});
jest.mock('../common/description/Description', () => { jest.mock('../common/description/Description', () => {
return jest.fn().mockReturnValue(<p>Description</p>); return jest.fn().mockReturnValue(<p>Description</p>);
}); });
@ -253,21 +249,6 @@ describe('Test MlModel entity detail component', () => {
expect(detailContainer).toBeInTheDocument(); expect(detailContainer).toBeInTheDocument();
}); });
it('Should render manage component for manage tab', async () => {
const { container } = render(
<MlModelDetailComponent {...mockProp} activeTab={5} />,
{
wrapper: MemoryRouter,
}
);
const detailContainer = await findByTestId(container, 'mlmodel-details');
const manageTab = await findByTestId(container, 'manage');
expect(detailContainer).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => { it('Check if active tab is custom properties', async () => {
const { container } = render( const { container } = render(
<MlModelDetailComponent {...mockProp} activeTab={4} />, <MlModelDetailComponent {...mockProp} activeTab={4} />,

View File

@ -30,6 +30,7 @@ import React, {
useState, useState,
} from 'react'; } from 'react';
import AppState from '../../AppState'; import AppState from '../../AppState';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { import {
getDashboardDetailsPath, getDashboardDetailsPath,
getServiceDetailsPath, getServiceDetailsPath,
@ -54,7 +55,6 @@ import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrum
import PageContainer from '../containers/PageContainer'; import PageContainer from '../containers/PageContainer';
import EntityLineageComponent from '../EntityLineage/EntityLineage.component'; import EntityLineageComponent from '../EntityLineage/EntityLineage.component';
import { Edge, EdgeData } from '../EntityLineage/EntityLineage.interface'; import { Edge, EdgeData } from '../EntityLineage/EntityLineage.interface';
import ManageTabComponent from '../ManageTab/ManageTab.component';
import MlModelFeaturesList from './MlModelFeaturesList'; import MlModelFeaturesList from './MlModelFeaturesList';
interface MlModelDetailProp extends HTMLAttributes<HTMLDivElement> { interface MlModelDetailProp extends HTMLAttributes<HTMLDivElement> {
@ -148,6 +148,12 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
? mlModelDetail.owner?.name ? mlModelDetail.owner?.name
: undefined, : undefined,
}, },
{
key: 'Tier',
value: mlModelTier?.tagFQN
? mlModelTier.tagFQN.split(FQN_SEPARATOR_CHAR)[1]
: '',
},
{ {
key: 'Algorithm', key: 'Algorithm',
value: mlModelDetail.algorithm, value: mlModelDetail.algorithm,
@ -222,18 +228,6 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
isProtected: false, isProtected: false,
position: 4, position: 4,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: false,
protectedState: !mlModelDetail.owner || hasEditAccess(),
position: 5,
},
]; ];
const setFollowersData = (followers: Array<EntityReference>) => { const setFollowersData = (followers: Array<EntityReference>) => {
@ -318,35 +312,6 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
} }
}; };
const onSettingsUpdate = (newOwner?: Mlmodel['owner'], newTier?: string) => {
if (newOwner || newTier) {
const tierTag: Mlmodel['tags'] = newTier
? [
...mlModelTags,
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: mlModelDetail.tags;
const updatedMlModelDetails = {
...mlModelDetail,
owner: newOwner
? {
...mlModelDetail.owner,
...newOwner,
}
: mlModelDetail.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedMlModelDetails);
} else {
return Promise.reject();
}
};
const onFeaturesUpdate = (features: Mlmodel['mlFeatures']) => { const onFeaturesUpdate = (features: Mlmodel['mlFeatures']) => {
updateMlModelFeatures({ ...mlModelDetail, mlFeatures: features }); updateMlModelFeatures({ ...mlModelDetail, mlFeatures: features });
}; };
@ -472,6 +437,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
isTagEditable isTagEditable
deleted={mlModelDetail.deleted} deleted={mlModelDetail.deleted}
entityFqn={mlModelDetail.fullyQualifiedName} entityFqn={mlModelDetail.fullyQualifiedName}
entityId={mlModelDetail.id}
entityName={mlModelDetail.name} entityName={mlModelDetail.name}
entityType={EntityType.MLMODEL} entityType={EntityType.MLMODEL}
extraInfo={mlModelPageInfo} extraInfo={mlModelPageInfo}
@ -553,23 +519,6 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
handleExtentionUpdate={onExtensionUpdate} handleExtentionUpdate={onExtensionUpdate}
/> />
)} )}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete
allowSoftDelete={!mlModelDetail.deleted}
currentTier={mlModelTier?.tagFQN}
currentUser={mlModelDetail.owner}
entityId={mlModelDetail.id}
entityName={mlModelDetail.name}
entityType={EntityType.MLMODEL}
hasEditAccess={hasEditAccess()}
hideOwner={mlModelDetail.deleted}
hideTier={mlModelDetail.deleted}
onSave={onSettingsUpdate}
/>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -51,7 +51,6 @@ import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer'; import PageContainer from '../containers/PageContainer';
import Entitylineage from '../EntityLineage/EntityLineage.component'; import Entitylineage from '../EntityLineage/EntityLineage.component';
import Loader from '../Loader/Loader'; import Loader from '../Loader/Loader';
import ManageTabComponent from '../ManageTab/ManageTab.component';
import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal';
import PipelineStatusList from '../PipelineStatusList/PipelineStatusList.component'; import PipelineStatusList from '../PipelineStatusList/PipelineStatusList.component';
@ -194,18 +193,6 @@ const PipelineDetails = ({
isProtected: false, isProtected: false,
position: 4, position: 4,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 5,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
@ -289,31 +276,6 @@ const PipelineDetails = ({
settingsUpdateHandler(updatedPipelineDetails); settingsUpdateHandler(updatedPipelineDetails);
} }
}; };
const onSettingsUpdate = (newOwner?: Pipeline['owner'], newTier?: string) => {
if (newOwner || newTier) {
const tierTag: Pipeline['tags'] = newTier
? [
...getTagsWithoutTier(pipelineDetails.tags as Array<EntityTags>),
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: pipelineDetails.tags;
const updatedPipelineDetails = {
...pipelineDetails,
owner: newOwner
? { ...pipelineDetails.owner, ...newOwner }
: pipelineDetails.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedPipelineDetails);
} else {
return Promise.reject();
}
};
const onTagUpdate = (selectedTags?: Array<EntityTags>) => { const onTagUpdate = (selectedTags?: Array<EntityTags>) => {
if (selectedTags) { if (selectedTags) {
@ -410,6 +372,7 @@ const PipelineDetails = ({
entityFieldThreadCount entityFieldThreadCount
)} )}
entityFqn={pipelineFQN} entityFqn={pipelineFQN}
entityId={pipelineDetails.id}
entityName={entityName} entityName={entityName}
entityType={EntityType.PIPELINE} entityType={EntityType.PIPELINE}
extraInfo={extraInfo} extraInfo={extraInfo}
@ -540,24 +503,6 @@ const PipelineDetails = ({
handleExtentionUpdate={onExtensionUpdate} handleExtentionUpdate={onExtensionUpdate}
/> />
)} )}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete
allowSoftDelete={!deleted}
currentTier={tier?.tagFQN}
currentUser={owner}
entityId={pipelineDetails.id}
entityName={pipelineDetails.name}
entityType={EntityType.PIPELINE}
hasEditAccess={hasEditAccess()}
hideOwner={deleted}
hideTier={deleted}
manageSectionType={EntityType.PIPELINE}
onSave={onSettingsUpdate}
/>
</div>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -136,10 +136,6 @@ window.IntersectionObserver = jest.fn().mockImplementation(() => ({
unobserve: mockunObserve, unobserve: mockunObserve,
})); }));
jest.mock('../ManageTab/ManageTab.component', () => {
return jest.fn().mockReturnValue(<p data-testid="manage">ManageTab</p>);
});
jest.mock('../common/description/Description', () => { jest.mock('../common/description/Description', () => {
return jest.fn().mockReturnValue(<p>Description Component</p>); return jest.fn().mockReturnValue(<p>Description Component</p>);
}); });
@ -210,7 +206,6 @@ describe('Test PipelineDetails component', () => {
const detailsTab = await findByTestId(tabs, 'Details'); const detailsTab = await findByTestId(tabs, 'Details');
const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks'); const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks');
const lineageTab = await findByTestId(tabs, 'Lineage'); const lineageTab = await findByTestId(tabs, 'Lineage');
const manageTab = await findByTestId(tabs, 'Manage');
expect(EntityPageInfo).toBeInTheDocument(); expect(EntityPageInfo).toBeInTheDocument();
expect(description).toBeInTheDocument(); expect(description).toBeInTheDocument();
@ -218,7 +213,6 @@ describe('Test PipelineDetails component', () => {
expect(detailsTab).toBeInTheDocument(); expect(detailsTab).toBeInTheDocument();
expect(activityFeedTab).toBeInTheDocument(); expect(activityFeedTab).toBeInTheDocument();
expect(lineageTab).toBeInTheDocument(); expect(lineageTab).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
}); });
it('Check if active tab is details', async () => { it('Check if active tab is details', async () => {
@ -273,18 +267,6 @@ describe('Test PipelineDetails component', () => {
expect(lineage).toBeInTheDocument(); expect(lineage).toBeInTheDocument();
}); });
it('Check if active tab is manage', async () => {
const { container } = render(
<PipelineDetails {...PipelineDetailsProps} activeTab={5} />,
{
wrapper: MemoryRouter,
}
);
const manage = await findByTestId(container, 'manage');
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => { it('Check if active tab is custom properties', async () => {
const { container } = render( const { container } = render(
<PipelineDetails {...PipelineDetailsProps} activeTab={4} />, <PipelineDetails {...PipelineDetailsProps} activeTab={4} />,

View File

@ -52,7 +52,6 @@ import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer'; import PageContainer from '../containers/PageContainer';
import EntityLineageComponent from '../EntityLineage/EntityLineage.component'; import EntityLineageComponent from '../EntityLineage/EntityLineage.component';
import Loader from '../Loader/Loader'; import Loader from '../Loader/Loader';
import ManageTabComponent from '../ManageTab/ManageTab.component';
import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../Modals/RequestDescriptionModal/RequestDescriptionModal';
import SampleDataTopic from '../SampleDataTopic/SampleDataTopic'; import SampleDataTopic from '../SampleDataTopic/SampleDataTopic';
import SchemaEditor from '../schema-editor/SchemaEditor'; import SchemaEditor from '../schema-editor/SchemaEditor';
@ -229,18 +228,6 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
isProtected: false, isProtected: false,
position: 6, position: 6,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 7,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
{ {
@ -319,34 +306,6 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
return Promise.reject(); return Promise.reject();
} }
}; };
const onSettingsUpdate = (newOwner?: Topic['owner'], newTier?: string) => {
if (newOwner || newTier) {
const tierTag: Topic['tags'] = newTier
? [
...getTagsWithoutTier(topicDetails.tags as Array<EntityTags>),
{
tagFQN: newTier,
labelType: LabelType.Manual,
state: State.Confirmed,
},
]
: topicDetails.tags;
const updatedTopicDetails = {
...topicDetails,
owner: newOwner
? {
...topicDetails.owner,
...newOwner,
}
: topicDetails.owner,
tags: tierTag,
};
return settingsUpdateHandler(updatedTopicDetails);
} else {
return Promise.reject();
}
};
const followTopic = () => { const followTopic = () => {
if (isFollowing) { if (isFollowing) {
@ -442,6 +401,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
entityFieldThreadCount entityFieldThreadCount
)} )}
entityFqn={topicFQN} entityFqn={topicFQN}
entityId={topicDetails.id}
entityName={entityName} entityName={entityName}
entityType={EntityType.TOPIC} entityType={EntityType.TOPIC}
extraInfo={extraInfo} extraInfo={extraInfo}
@ -573,24 +533,6 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
handleExtentionUpdate={onExtensionUpdate} handleExtentionUpdate={onExtensionUpdate}
/> />
)} )}
{activeTab === 7 && (
<div>
<ManageTabComponent
allowDelete
allowSoftDelete={!deleted}
currentTier={tier?.tagFQN}
currentUser={owner}
entityId={topicDetails.id}
entityName={topicDetails.name}
entityType={EntityType.TOPIC}
hasEditAccess={hasEditAccess()}
hideOwner={deleted}
hideTier={deleted}
manageSectionType={EntityType.TOPIC}
onSave={onSettingsUpdate}
/>
</div>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -122,10 +122,6 @@ window.IntersectionObserver = jest.fn().mockImplementation(() => ({
unobserve: mockunObserve, unobserve: mockunObserve,
})); }));
jest.mock('../ManageTab/ManageTab.component', () => {
return jest.fn().mockReturnValue(<p data-testid="manage">ManageTab</p>);
});
jest.mock('../EntityLineage/EntityLineage.component', () => { jest.mock('../EntityLineage/EntityLineage.component', () => {
return jest.fn().mockReturnValue(<p>EntityLineage.component</p>); return jest.fn().mockReturnValue(<p>EntityLineage.component</p>);
}); });
@ -183,7 +179,6 @@ describe('Test TopicDetails component', () => {
const schemaTab = await findByTestId(tabs, 'Schema'); const schemaTab = await findByTestId(tabs, 'Schema');
const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks'); const activityFeedTab = await findByTestId(tabs, 'Activity Feed & Tasks');
const configTab = await findByTestId(tabs, 'Config'); const configTab = await findByTestId(tabs, 'Config');
const manageTab = await findByTestId(tabs, 'Manage');
expect(EntityPageInfo).toBeInTheDocument(); expect(EntityPageInfo).toBeInTheDocument();
expect(description).toBeInTheDocument(); expect(description).toBeInTheDocument();
@ -191,7 +186,6 @@ describe('Test TopicDetails component', () => {
expect(schemaTab).toBeInTheDocument(); expect(schemaTab).toBeInTheDocument();
expect(activityFeedTab).toBeInTheDocument(); expect(activityFeedTab).toBeInTheDocument();
expect(configTab).toBeInTheDocument(); expect(configTab).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
}); });
it('Check if active tab is schema', async () => { it('Check if active tab is schema', async () => {
@ -239,18 +233,6 @@ describe('Test TopicDetails component', () => {
expect(config).toBeInTheDocument(); expect(config).toBeInTheDocument();
}); });
it('Check if active tab is manage', async () => {
const { container } = render(
<TopicDetails {...TopicDetailsProps} activeTab={7} />,
{
wrapper: MemoryRouter,
}
);
const manage = await findByTestId(container, 'manage');
expect(manage).toBeInTheDocument();
});
it('Should render lineage tab', async () => { it('Should render lineage tab', async () => {
const { container } = render( const { container } = render(
<TopicDetails {...TopicDetailsProps} activeTab={5} />, <TopicDetails {...TopicDetailsProps} activeTab={5} />,

View File

@ -15,6 +15,7 @@ export interface DeleteWidgetModalProps {
visible: boolean; visible: boolean;
onCancel: () => void; onCancel: () => void;
allowSoftDelete?: boolean; allowSoftDelete?: boolean;
deleteMessage?: string;
entityName: string; entityName: string;
entityType: string; entityType: string;
isAdminUser?: boolean; isAdminUser?: boolean;

View File

@ -30,6 +30,7 @@ import { DeleteType, DeleteWidgetModalProps } from './DeleteWidget.interface';
const DeleteWidgetModal = ({ const DeleteWidgetModal = ({
allowSoftDelete = true, allowSoftDelete = true,
visible, visible,
deleteMessage,
entityName, entityName,
entityType, entityType,
onCancel, onCancel,
@ -61,7 +62,7 @@ const DeleteWidgetModal = ({
}, },
{ {
title: `Permanently Delete ${entityType}${entityName}`, title: `Permanently Delete ${entityType}${entityName}`,
description: prepareDeleteMessage(), description: deleteMessage || prepareDeleteMessage(),
type: DeleteType.HARD_DELETE, type: DeleteType.HARD_DELETE,
isAllowd: true, isAllowd: true,
}, },
@ -222,7 +223,9 @@ const DeleteWidgetModal = ({
data-testid={option.type} data-testid={option.type}
key={option.type} key={option.type}
value={option.type}> value={option.type}>
<p className="tw-text-sm tw-mb-1 tw-font-medium"> <p
className="tw-text-sm tw-mb-1 tw-font-medium"
data-testid={`${option.type}-option`}>
{option.title} {option.title}
</p> </p>
<p className="tw-text-grey-muted tw-text-xs tw-mb-2"> <p className="tw-text-grey-muted tw-text-xs tw-mb-2">

View File

@ -49,7 +49,9 @@ const EntitySummaryDetails = ({
) : ( ) : (
<> <>
No Owner No Owner
<span onClick={() => setshow(!show)}> <span
data-testid={`edit-${data.key}-icon`}
onClick={() => setshow(!show)}>
{updateOwner ? ( {updateOwner ? (
<SVGIcons <SVGIcons
alt="edit" alt="edit"
@ -78,7 +80,9 @@ const EntitySummaryDetails = ({
/> />
} }
trigger={['click']}> trigger={['click']}>
<span style={{ marginLeft: '5px' }}> <span
data-testid={`edit-${data.key}-icon`}
style={{ marginLeft: '5px' }}>
{updateTier ? ( {updateTier ? (
<SVGIcons <SVGIcons
alt="edit" alt="edit"
@ -147,7 +151,10 @@ const EntitySummaryDetails = ({
)} )}
</> </>
</a> </a>
<span style={{ marginLeft: '5px' }} onClick={() => setshow(true)}> <span
data-testid={`edit-${data.key}-icon`}
style={{ marginLeft: '5px' }}
onClick={() => setshow(true)}>
<SVGIcons <SVGIcons
alt="edit" alt="edit"
icon={Icons.EDIT} icon={Icons.EDIT}
@ -175,6 +182,7 @@ const EntitySummaryDetails = ({
{displayVal} {displayVal}
</Button> </Button>
<span <span
data-testid={`edit-${data.key}-icon`}
style={{ marginLeft: '5px' }} style={{ marginLeft: '5px' }}
onClick={() => setshow(true)}> onClick={() => setshow(true)}>
{updateOwner ? ( {updateOwner ? (
@ -215,7 +223,9 @@ const EntitySummaryDetails = ({
/> />
} }
trigger={['click']}> trigger={['click']}>
<span style={{ marginLeft: '5px' }}> <span
data-testid={`edit-${data.key}-icon`}
style={{ marginLeft: '5px' }}>
{updateTier ? ( {updateTier ? (
<SVGIcons <SVGIcons
alt="edit" alt="edit"

View File

@ -20,17 +20,23 @@ import SVGIcons, { Icons } from '../../../../utils/SvgUtils';
import DeleteWidgetModal from '../../DeleteWidget/DeleteWidgetModal'; import DeleteWidgetModal from '../../DeleteWidget/DeleteWidgetModal';
interface Props { interface Props {
allowSoftDelete?: boolean;
entityName: string; entityName: string;
entityId?: string; entityId?: string;
entityType?: string; entityType?: string;
entityFQN?: string; entityFQN?: string;
onAnnouncementClick: () => void; isRecursiveDelete?: boolean;
deleteMessage?: string;
onAnnouncementClick?: () => void;
} }
const ManageButton: FC<Props> = ({ const ManageButton: FC<Props> = ({
allowSoftDelete,
deleteMessage,
entityName, entityName,
entityType, entityType,
entityId, entityId,
isRecursiveDelete,
onAnnouncementClick, onAnnouncementClick,
}) => { }) => {
const [showActions, setShowActions] = useState<boolean>(false); const [showActions, setShowActions] = useState<boolean>(false);
@ -51,7 +57,7 @@ const ManageButton: FC<Props> = ({
}}> }}>
<SVGIcons alt="Delete" icon={Icons.DELETE_GRADIANT} /> <SVGIcons alt="Delete" icon={Icons.DELETE_GRADIANT} />
<div className="tw-text-left" data-testid="delete-button"> <div className="tw-text-left" data-testid="delete-button">
<p className="tw-font-medium"> <p className="tw-font-medium" data-testid="delete-button-title">
Delete {entityType} {entityName} Delete {entityType} {entityName}
</p> </p>
<p className="tw-text-grey-muted tw-text-xs"> <p className="tw-text-grey-muted tw-text-xs">
@ -73,7 +79,7 @@ const ManageButton: FC<Props> = ({
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setShowActions(false); setShowActions(false);
onAnnouncementClick(); onAnnouncementClick && onAnnouncementClick();
}}> }}>
<SVGIcons alt="Delete" icon={Icons.ANNOUNCEMENT} /> <SVGIcons alt="Delete" icon={Icons.ANNOUNCEMENT} />
<div <div
@ -120,9 +126,12 @@ const ManageButton: FC<Props> = ({
</Dropdown> </Dropdown>
{isDelete && ( {isDelete && (
<DeleteWidgetModal <DeleteWidgetModal
allowSoftDelete={allowSoftDelete}
deleteMessage={deleteMessage}
entityId={entityId || ''} entityId={entityId || ''}
entityName={entityName || ''} entityName={entityName || ''}
entityType={entityType || ''} entityType={entityType || ''}
isRecursiveDelete={isRecursiveDelete}
visible={isDelete} visible={isDelete}
onCancel={() => setIsDelete(false)} onCancel={() => setIsDelete(false)}
/> />

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Space } from 'antd';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
@ -40,6 +41,7 @@ import {
import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList'; import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import Description from '../../components/common/description/Description'; import Description from '../../components/common/description/Description';
import ManageButton from '../../components/common/entityPageInfo/ManageButton/ManageButton';
import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails'; import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import RichTextEditorPreviewer from '../../components/common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from '../../components/common/rich-text-editor/RichTextEditorPreviewer';
@ -48,7 +50,6 @@ import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-brea
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 ManageTabComponent from '../../components/ManageTab/ManageTab.component';
import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants'; import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { import {
@ -70,12 +71,9 @@ import { EntityReference } from '../../generated/entity/teams/user';
import { useInfiniteScroll } from '../../hooks/useInfiniteScroll'; import { useInfiniteScroll } from '../../hooks/useInfiniteScroll';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { import {
getEntityDeleteMessage,
getEntityName, getEntityName,
getPartialNameFromTableFQN, getPartialNameFromTableFQN,
hasEditAccess,
isEven, isEven,
pluralize,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { import {
databaseSchemaDetailsTabs, databaseSchemaDetailsTabs,
@ -159,17 +157,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
position: 2, position: 2,
count: feedCount, count: feedCount,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: false,
position: 3,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
@ -583,17 +570,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
); );
}; };
const getDeleteEntityMessage = () => {
if (!tableInstanceCount) {
return;
}
return getEntityDeleteMessage(
'Database Schema',
pluralize(tableInstanceCount, 'Table')
);
};
useEffect(() => { useEffect(() => {
if (TabSpecificField.ACTIVITY_FEED === tab) { if (TabSpecificField.ACTIVITY_FEED === tab) {
fetchActivityFeed(); fetchActivityFeed();
@ -639,12 +615,28 @@ const DatabaseSchemaPage: FunctionComponent = () => {
<div <div
className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col" className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"
data-testid="page-container"> data-testid="page-container">
<TitleBreadcrumb titleLinks={slashedTableName} /> <Space
align="center"
className="tw-justify-between"
style={{ width: '100%' }}>
<TitleBreadcrumb titleLinks={slashedTableName} />
<ManageButton
isRecursiveDelete
allowSoftDelete={false}
entityFQN={databaseSchemaFQN}
entityId={databaseSchemaId}
entityName={databaseSchemaName}
entityType={EntityType.DATABASE_SCHEMA}
/>
</Space>
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap"> <div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap">
{extraInfo.map((info, index) => ( {extraInfo.map((info, index) => (
<span className="tw-flex" key={index}> <span className="tw-flex" key={index}>
<EntitySummaryDetails data={info} /> <EntitySummaryDetails
data={info}
updateOwner={handleUpdateOwner}
/>
{extraInfo.length !== 1 && index < extraInfo.length - 1 ? ( {extraInfo.length !== 1 && index < extraInfo.length - 1 ? (
<span className="tw-mx-1.5 tw-inline-block tw-text-gray-400"> <span className="tw-mx-1.5 tw-inline-block tw-text-gray-400">
@ -703,24 +695,6 @@ const DatabaseSchemaPage: FunctionComponent = () => {
<div /> <div />
</div> </div>
)} )}
{activeTab === 3 && (
<ManageTabComponent
allowDelete
hideTier
isRecursiveDelete
currentUser={databaseSchema?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={databaseSchema?.id}
entityName={databaseSchema?.name}
entityType={EntityType.DATABASE_SCHEMA}
hasEditAccess={hasEditAccess(
databaseSchema?.owner?.type || '',
databaseSchema?.owner?.id || ''
)}
manageSectionType={EntityType.DATABASE_SCHEMA}
onSave={handleUpdateOwner}
/>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Space } from 'antd';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
@ -38,10 +39,10 @@ import {
postFeedById, postFeedById,
postThread, postThread,
} from '../../axiosAPIs/feedsAPI'; } from '../../axiosAPIs/feedsAPI';
import { getAllTables } from '../../axiosAPIs/tableAPI';
import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList'; import ActivityFeedList from '../../components/ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import Description from '../../components/common/description/Description'; import Description from '../../components/common/description/Description';
import ManageButton from '../../components/common/entityPageInfo/ManageButton/ManageButton';
import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails'; import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import NextPrevious from '../../components/common/next-previous/NextPrevious'; import NextPrevious from '../../components/common/next-previous/NextPrevious';
@ -51,7 +52,6 @@ import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-brea
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 ManageTabComponent from '../../components/ManageTab/ManageTab.component';
import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal'; import RequestDescriptionModal from '../../components/Modals/RequestDescriptionModal/RequestDescriptionModal';
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants'; import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
import { import {
@ -75,13 +75,7 @@ import { EntityReference } from '../../generated/entity/teams/user';
import { Paging } from '../../generated/type/paging'; import { Paging } from '../../generated/type/paging';
import { useInfiniteScroll } from '../../hooks/useInfiniteScroll'; import { useInfiniteScroll } from '../../hooks/useInfiniteScroll';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { import { getEntityName, isEven } from '../../utils/CommonUtils';
getEntityDeleteMessage,
getEntityName,
hasEditAccess,
isEven,
pluralize,
} from '../../utils/CommonUtils';
import { import {
databaseDetailsTabs, databaseDetailsTabs,
getCurrentDatabaseDetailsTab, getCurrentDatabaseDetailsTab,
@ -124,7 +118,6 @@ const DatabaseDetails: FunctionComponent = () => {
useState<Paging>(pagingObject); useState<Paging>(pagingObject);
const [databaseSchemaInstanceCount, setSchemaInstanceCount] = const [databaseSchemaInstanceCount, setSchemaInstanceCount] =
useState<number>(0); useState<number>(0);
const [tableInstanceCount, setTableInstanceCount] = useState<number>(0);
const [activeTab, setActiveTab] = useState<number>( const [activeTab, setActiveTab] = useState<number>(
getCurrentDatabaseDetailsTab(tab) getCurrentDatabaseDetailsTab(tab)
@ -173,17 +166,6 @@ const DatabaseDetails: FunctionComponent = () => {
position: 2, position: 2,
count: feedCount, count: feedCount,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: false,
position: 3,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
@ -549,25 +531,6 @@ const DatabaseDetails: FunctionComponent = () => {
updateThreadData(threadId, postId, isThread, data, setEntityThread); updateThreadData(threadId, postId, isThread, data, setEntityThread);
}; };
const fetchTablesCount = () => {
// limit=0 will fetch empty data list with total count
getAllTables('', 0, databaseFQN)
.then((res: AxiosResponse) => {
if (res.data) {
setTableInstanceCount(res.data.paging.total);
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['unexpected-server-response']
);
setTableInstanceCount(0);
});
};
const getLoader = () => { const getLoader = () => {
return isentityThreadLoading ? <Loader /> : null; return isentityThreadLoading ? <Loader /> : null;
}; };
@ -582,25 +545,8 @@ const DatabaseDetails: FunctionComponent = () => {
} }
}; };
const getDeleteEntityMessage = () => {
if (!databaseSchemaInstanceCount && !tableInstanceCount) {
return;
}
const counts = [];
if (databaseSchemaInstanceCount) {
counts.push(pluralize(databaseSchemaInstanceCount, 'Schema'));
}
if (tableInstanceCount) {
counts.push(pluralize(tableInstanceCount, 'Table'));
}
return getEntityDeleteMessage('Database', counts.join(' and '));
};
useEffect(() => { useEffect(() => {
getEntityFeedCount(); getEntityFeedCount();
fetchTablesCount();
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -656,12 +602,28 @@ const DatabaseDetails: FunctionComponent = () => {
<div <div
className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col" className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"
data-testid="page-container"> data-testid="page-container">
<TitleBreadcrumb titleLinks={slashedDatabaseName} /> <Space
align="center"
className="tw-justify-between"
style={{ width: '100%' }}>
<TitleBreadcrumb titleLinks={slashedDatabaseName} />
<ManageButton
isRecursiveDelete
allowSoftDelete={false}
entityFQN={databaseFQN}
entityId={databaseId}
entityName={databaseName}
entityType={EntityType.DATABASE}
/>
</Space>
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap"> <div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap">
{extraInfo.map((info, index) => ( {extraInfo.map((info, index) => (
<span className="tw-flex" key={index}> <span className="tw-flex" key={index}>
<EntitySummaryDetails data={info} /> <EntitySummaryDetails
data={info}
updateOwner={handleUpdateOwner}
/>
{extraInfo.length !== 1 && index < extraInfo.length - 1 ? ( {extraInfo.length !== 1 && index < extraInfo.length - 1 ? (
<span className="tw-mx-1.5 tw-inline-block tw-text-gray-400"> <span className="tw-mx-1.5 tw-inline-block tw-text-gray-400">
@ -819,24 +781,6 @@ const DatabaseDetails: FunctionComponent = () => {
<div /> <div />
</div> </div>
)} )}
{activeTab === 3 && (
<ManageTabComponent
allowDelete
hideTier
isRecursiveDelete
currentUser={database?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={database?.id}
entityName={database?.name}
entityType={EntityType.DATABASE}
hasEditAccess={hasEditAccess(
database?.owner?.type || '',
database?.owner?.id || ''
)}
manageSectionType={EntityType.DATABASE}
onSave={handleUpdateOwner}
/>
)}
<div <div
data-testid="observer-element" data-testid="observer-element"
id="observer-element" id="observer-element"

View File

@ -92,24 +92,54 @@ jest.mock('../../axiosAPIs/ingestionPipelineAPI', () => ({
}, },
}) })
), ),
deleteIngestionPipelineById: jest.fn(), checkAirflowStatus: jest.fn().mockImplementation(() => {
addIngestionPipeline: jest.fn(), Promise.resolve();
triggerIngestionPipelineById: jest.fn(), }),
updateIngestionPipeline: jest.fn(), deployIngestionPipelineById: jest.fn().mockImplementation(() => {
Promise.resolve();
}),
deleteIngestionPipelineById: jest.fn().mockImplementation(() => {
Promise.resolve();
}),
enableDisableIngestionPipelineById: jest.fn().mockImplementation(() => {
Promise.resolve();
}),
triggerIngestionPipelineById: jest.fn().mockImplementation(() => {
Promise.resolve();
}),
}));
jest.mock('../../axiosAPIs/miscAPI', () => ({
fetchAirflowConfig: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../axiosAPIs/mlModelAPI', () => ({
getMlmodels: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../axiosAPIs/pipelineAPI', () => ({
getPipelines: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../axiosAPIs/topicsAPI', () => ({
getTopics: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../axiosAPIs/dashboardAPI', () => ({
getDashboards: jest.fn().mockImplementation(() => Promise.resolve()),
})); }));
jest.mock('../../axiosAPIs/serviceAPI', () => ({ jest.mock('../../axiosAPIs/serviceAPI', () => ({
getServiceByFQN: jest getServiceByFQN: jest
.fn() .fn()
.mockImplementation(() => Promise.resolve({ data: mockData })), .mockImplementation(() => Promise.resolve({ data: mockData })),
updateService: jest.fn(), updateService: jest.fn().mockImplementation(() => Promise.resolve()),
})); }));
jest.mock('../../axiosAPIs/databaseAPI', () => ({ jest.mock('../../axiosAPIs/databaseAPI', () => ({
getDatabases: jest getDatabases: jest
.fn() .fn()
.mockImplementation(() => Promise.resolve({ data: mockDatabase })), .mockImplementation(() => Promise.resolve({ data: mockDatabase })),
updateService: jest.fn(),
})); }));
jest.mock( jest.mock(
@ -155,6 +185,7 @@ jest.mock('../../utils/ServiceUtils', () => ({
getServiceCategoryFromType: jest.fn().mockReturnValue('databaseServices'), getServiceCategoryFromType: jest.fn().mockReturnValue('databaseServices'),
serviceTypeLogo: jest.fn().mockReturnValue('img/path'), serviceTypeLogo: jest.fn().mockReturnValue('img/path'),
isRequiredDetailsAvailableForIngestion: jest.fn().mockReturnValue(true), isRequiredDetailsAvailableForIngestion: jest.fn().mockReturnValue(true),
getDeleteEntityMessage: jest.fn().mockReturnValue('Delete message'),
})); }));
jest.mock( jest.mock(
@ -185,6 +216,26 @@ jest.mock('../../components/ServiceConfig/ServiceConfig', () => {
return jest.fn().mockReturnValue(<p>ServiceConfig</p>); return jest.fn().mockReturnValue(<p>ServiceConfig</p>);
}); });
jest.mock(
'../../components/common/entityPageInfo/ManageButton/ManageButton',
() => {
return jest.fn().mockReturnValue(<p>ManageButton</p>);
}
);
jest.mock('antd', () => ({
Space: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
}));
jest.mock('../../utils/TableUtils', () => ({
getEntityLink: jest.fn(),
getUsagePercentile: jest.fn(),
}));
jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
}));
describe('Test ServicePage Component', () => { describe('Test ServicePage Component', () => {
it('Component should render', async () => { it('Component should render', async () => {
const { container } = render(<ServicePage />, { const { container } = render(<ServicePage />, {

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Space } from 'antd';
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { isNil, isUndefined, startCase } from 'lodash'; import { isNil, isUndefined, startCase } from 'lodash';
@ -35,6 +36,7 @@ import { getServiceByFQN, updateService } from '../../axiosAPIs/serviceAPI';
import { getTopics } from '../../axiosAPIs/topicsAPI'; import { getTopics } from '../../axiosAPIs/topicsAPI';
import { Button } from '../../components/buttons/Button/Button'; import { Button } from '../../components/buttons/Button/Button';
import Description from '../../components/common/description/Description'; import Description from '../../components/common/description/Description';
import ManageButton from '../../components/common/entityPageInfo/ManageButton/ManageButton';
import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails'; import EntitySummaryDetails from '../../components/common/EntitySummaryDetails/EntitySummaryDetails';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import ErrorPlaceHolderIngestion from '../../components/common/error-with-placeholder/ErrorPlaceHolderIngestion'; import ErrorPlaceHolderIngestion from '../../components/common/error-with-placeholder/ErrorPlaceHolderIngestion';
@ -46,7 +48,6 @@ import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/t
import PageContainer from '../../components/containers/PageContainer'; import PageContainer from '../../components/containers/PageContainer';
import Ingestion from '../../components/Ingestion/Ingestion.component'; import Ingestion from '../../components/Ingestion/Ingestion.component';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import ManageTabComponent from '../../components/ManageTab/ManageTab.component';
import ServiceConnectionDetails from '../../components/ServiceConnectionDetails/ServiceConnectionDetails.component'; import ServiceConnectionDetails from '../../components/ServiceConnectionDetails/ServiceConnectionDetails.component';
import TagsViewer from '../../components/tags-viewer/tags-viewer'; import TagsViewer from '../../components/tags-viewer/tags-viewer';
import { import {
@ -55,7 +56,6 @@ import {
PAGE_SIZE, PAGE_SIZE,
pagingObject, pagingObject,
} from '../../constants/constants'; } from '../../constants/constants';
import { ADMIN_ONLY_ACCESSIBLE_SECTION } from '../../enums/common.enum';
import { SearchIndex } from '../../enums/search.enum'; import { SearchIndex } from '../../enums/search.enum';
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { OwnerType } from '../../enums/user.enum'; import { OwnerType } from '../../enums/user.enum';
@ -72,12 +72,9 @@ import { useAuth } from '../../hooks/authHooks';
import { ConfigData, ServiceDataObj } from '../../interface/service.interface'; import { ConfigData, ServiceDataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { import {
getEntityDeleteMessage,
getEntityMissingError, getEntityMissingError,
getEntityName, getEntityName,
hasEditAccess,
isEven, isEven,
pluralize,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { import {
getEditConnectionPath, getEditConnectionPath,
@ -85,6 +82,7 @@ import {
} from '../../utils/RouterUtils'; } from '../../utils/RouterUtils';
import { import {
getCurrentServiceTab, getCurrentServiceTab,
getDeleteEntityMessage,
getServiceCategoryFromType, getServiceCategoryFromType,
servicePageTabs, servicePageTabs,
serviceTypeLogo, serviceTypeLogo,
@ -184,17 +182,6 @@ const ServicePage: FunctionComponent = () => {
isHidden: !isAdminUser && !isAuthDisabled, isHidden: !isAdminUser && !isAuthDisabled,
position: 3, position: 3,
}, },
{
name: 'Manage',
icon: {
alt: 'manage',
name: 'icon-manage',
title: 'Manage',
selectedName: 'icon-managecolor',
},
isProtected: false,
position: 4,
},
]; ];
const extraInfo: Array<ExtraInfo> = [ const extraInfo: Array<ExtraInfo> = [
@ -686,42 +673,6 @@ const ServicePage: FunctionComponent = () => {
} }
}; };
const getDeleteEntityMessage = () => {
const service = serviceName?.slice(0, -1);
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
`${pluralize(instanceCount, 'Database')}, ${pluralize(
schemaCount,
'Schema'
)} and ${pluralize(tableCount, 'Table')}`
);
case ServiceCategory.MESSAGING_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Topic')
);
case ServiceCategory.DASHBOARD_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Dashboard')
);
case ServiceCategory.PIPELINE_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Pipeline')
);
default:
return;
}
};
useEffect(() => { useEffect(() => {
setServiceName(serviceCategory || getServiceCategoryFromType(serviceType)); setServiceName(serviceCategory || getServiceCategoryFromType(serviceType));
}, [serviceCategory, serviceType]); }, [serviceCategory, serviceType]);
@ -932,7 +883,26 @@ const ServicePage: FunctionComponent = () => {
<div <div
className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col" className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"
data-testid="service-page"> data-testid="service-page">
<TitleBreadcrumb titleLinks={slashedTableName} /> <Space
align="center"
className="tw-justify-between"
style={{ width: '100%' }}>
<TitleBreadcrumb titleLinks={slashedTableName} />
<ManageButton
isRecursiveDelete
allowSoftDelete={false}
deleteMessage={getDeleteEntityMessage(
serviceName || '',
instanceCount,
schemaCount,
tableCount
)}
entityFQN={serviceFQN}
entityId={serviceDetails?.id}
entityName={serviceDetails?.name || ''}
entityType={serviceName?.slice(0, -1)}
/>
</Space>
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap"> <div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap">
{extraInfo.map((info, index) => ( {extraInfo.map((info, index) => (
@ -1075,27 +1045,6 @@ const ServicePage: FunctionComponent = () => {
/> />
</> </>
)} )}
{activeTab === 4 && (
<div className="tw-bg-white tw-h-full tw-pt-4 tw-pb-6">
<ManageTabComponent
allowDelete
hideTier
isRecursiveDelete
currentUser={serviceDetails?.owner}
deletEntityMessage={getDeleteEntityMessage()}
entityId={serviceDetails?.id}
entityName={serviceDetails?.name}
entityType={serviceCategory.slice(0, -1)}
hasEditAccess={hasEditAccess(
serviceDetails?.owner?.type || '',
serviceDetails?.owner?.id || ''
)}
manageSectionType={ADMIN_ONLY_ACCESSIBLE_SECTION.SERVICE}
onSave={handleUpdateOwner}
/>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -35,10 +35,6 @@ export const dashboardDetailsTabs = [
name: 'Custom Properties', name: 'Custom Properties',
path: 'custom_properties', path: 'custom_properties',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentDashboardTab = (tab: string) => { export const getCurrentDashboardTab = (tab: string) => {
@ -59,11 +55,6 @@ export const getCurrentDashboardTab = (tab: string) => {
break; break;
case 'manage':
currentTab = 5;
break;
case 'details': case 'details':
default: default:
currentTab = 1; currentTab = 1;

View File

@ -10,10 +10,6 @@ export const databaseDetailsTabs = [
path: 'activity_feed', path: 'activity_feed',
field: TabSpecificField.ACTIVITY_FEED, field: TabSpecificField.ACTIVITY_FEED,
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentDatabaseDetailsTab = (tab: string) => { export const getCurrentDatabaseDetailsTab = (tab: string) => {
@ -22,10 +18,6 @@ export const getCurrentDatabaseDetailsTab = (tab: string) => {
case 'activity_feed': case 'activity_feed':
currentTab = 2; currentTab = 2;
break;
case 'manage':
currentTab = 3;
break; break;
case 'schemas': case 'schemas':

View File

@ -10,10 +10,6 @@ export const databaseSchemaDetailsTabs = [
path: 'activity_feed', path: 'activity_feed',
field: TabSpecificField.ACTIVITY_FEED, field: TabSpecificField.ACTIVITY_FEED,
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentDatabaseSchemaDetailsTab = (tab: string) => { export const getCurrentDatabaseSchemaDetailsTab = (tab: string) => {
@ -22,10 +18,6 @@ export const getCurrentDatabaseSchemaDetailsTab = (tab: string) => {
case 'activity_feed': case 'activity_feed':
currentTab = 2; currentTab = 2;
break;
case 'manage':
currentTab = 3;
break; break;
case 'tables': case 'tables':

View File

@ -58,10 +58,6 @@ export const datasetTableTabs = [
name: 'Custom Properties', name: 'Custom Properties',
path: 'custom_properties', path: 'custom_properties',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentDatasetTab = (tab: string) => { export const getCurrentDatasetTab = (tab: string) => {
@ -103,10 +99,6 @@ export const getCurrentDatasetTab = (tab: string) => {
case 'custom_properties': case 'custom_properties':
currentTab = 9; currentTab = 9;
break;
case 'manage':
currentTab = 10;
break; break;
case 'schema': case 'schema':

View File

@ -34,10 +34,6 @@ export const mlModelTabs = [
name: 'Custom Properties', name: 'Custom Properties',
path: 'custom_properties', path: 'custom_properties',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentMlModelTab = (tab: string) => { export const getCurrentMlModelTab = (tab: string) => {
@ -55,10 +51,7 @@ export const getCurrentMlModelTab = (tab: string) => {
currentTab = 4; currentTab = 4;
break; break;
case 'manage':
currentTab = 5;
break;
case 'features': case 'features':
currentTab = 1; currentTab = 1;

View File

@ -41,10 +41,6 @@ export const pipelineDetailsTabs = [
name: 'Custom Properties', name: 'Custom Properties',
path: 'custom_properties', path: 'custom_properties',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentPipelineTab = (tab: string) => { export const getCurrentPipelineTab = (tab: string) => {
@ -64,11 +60,6 @@ export const getCurrentPipelineTab = (tab: string) => {
break; break;
case 'manage':
currentTab = 5;
break;
case 'details': case 'details':
default: default:
currentTab = 1; currentTab = 1;

View File

@ -97,6 +97,7 @@ import {
PipelineServiceType, PipelineServiceType,
} from '../generated/entity/services/pipelineService'; } from '../generated/entity/services/pipelineService';
import { DataService, ServiceResponse } from '../interface/service.interface'; import { DataService, ServiceResponse } from '../interface/service.interface';
import { getEntityDeleteMessage, pluralize } from './CommonUtils';
import { getDashboardURL } from './DashboardServiceUtils'; import { getDashboardURL } from './DashboardServiceUtils';
import { getBrokers } from './MessagingServiceUtils'; import { getBrokers } from './MessagingServiceUtils';
import { showErrorToast } from './ToastUtils'; import { showErrorToast } from './ToastUtils';
@ -434,10 +435,6 @@ export const servicePageTabs = (entity: string) => [
name: 'Connection', name: 'Connection',
path: 'connection', path: 'connection',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentServiceTab = (tab: string) => { export const getCurrentServiceTab = (tab: string) => {
@ -453,11 +450,6 @@ export const getCurrentServiceTab = (tab: string) => {
break; break;
case 'manage':
currentTab = 4;
break;
case 'entity': case 'entity':
default: default:
currentTab = 1; currentTab = 1;
@ -731,3 +723,44 @@ export const getOptionalFields = (
} }
} }
}; };
export const getDeleteEntityMessage = (
serviceName: string,
instanceCount: number,
schemaCount: number,
tableCount: number
) => {
const service = serviceName?.slice(0, -1);
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
`${pluralize(instanceCount, 'Database')}, ${pluralize(
schemaCount,
'Schema'
)} and ${pluralize(tableCount, 'Table')}`
);
case ServiceCategory.MESSAGING_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Topic')
);
case ServiceCategory.DASHBOARD_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Dashboard')
);
case ServiceCategory.PIPELINE_SERVICES:
return getEntityDeleteMessage(
service || 'Service',
pluralize(instanceCount, 'Pipeline')
);
default:
return;
}
};

View File

@ -41,10 +41,6 @@ export const topicDetailsTabs = [
name: 'Custom Properties', name: 'Custom Properties',
path: 'custom_properties', path: 'custom_properties',
}, },
{
name: 'Manage',
path: 'manage',
},
]; ];
export const getCurrentTopicTab = (tab: string) => { export const getCurrentTopicTab = (tab: string) => {
@ -69,10 +65,6 @@ export const getCurrentTopicTab = (tab: string) => {
case 'custom_properties': case 'custom_properties':
currentTab = 6; currentTab = 6;
break;
case 'manage':
currentTab = 7;
break; break;
case 'schema': case 'schema':