mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-01 19:18:05 +00:00
fix: edit description permission for domain owner (#19475)
* fix: edit description permission for domain owner * fix: usage of permissions prop * test: fix unit test and add e2e test for edit description * refactor: remove editCustomAttributePermission and viewAllPermission props * fix: e2e test for edit description * fix: playwright tests * fix: update e2e test for edit description (cherry picked from commit 4059df8843afe472f27b3712a2a7ef562f1f5f10)
This commit is contained in:
parent
95501d850e
commit
4abe64087a
@ -10,7 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import test, { expect } from '@playwright/test';
|
||||
import base, { expect, Page } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { get } from 'lodash';
|
||||
import { SidebarItem } from '../../constant/sidebar';
|
||||
@ -40,11 +40,53 @@ import {
|
||||
import { sidebarClick } from '../../utils/sidebar';
|
||||
import { performUserLogin, visitUserProfilePage } from '../../utils/user';
|
||||
|
||||
test.describe('Domains', () => {
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
const user = new UserClass();
|
||||
|
||||
const domain = new Domain();
|
||||
|
||||
const test = base.extend<{
|
||||
page: Page;
|
||||
userPage: Page;
|
||||
}>({
|
||||
page: async ({ browser }, use) => {
|
||||
const { page } = await performAdminLogin(browser);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
userPage: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await user.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
test.describe('Domains', () => {
|
||||
test.slow(true);
|
||||
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.create(apiContext);
|
||||
|
||||
await domain.create(apiContext);
|
||||
|
||||
await domain.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/owners/0',
|
||||
value: {
|
||||
id: user.responseData.id,
|
||||
type: 'user',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach('Visit home page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
});
|
||||
@ -389,6 +431,41 @@ test.describe('Domains', () => {
|
||||
await afterAction();
|
||||
}
|
||||
});
|
||||
|
||||
test('Domain owner should able to edit description of domain', async ({
|
||||
page,
|
||||
userPage,
|
||||
}) => {
|
||||
const { afterAction, apiContext } = await getApiContext(page);
|
||||
try {
|
||||
await sidebarClick(userPage, SidebarItem.DOMAIN);
|
||||
await selectDomain(userPage, domain.data);
|
||||
|
||||
await expect(userPage.getByTestId('edit-description')).toBeInViewport();
|
||||
|
||||
await userPage.getByTestId('edit-description').click();
|
||||
|
||||
await expect(userPage.getByTestId('editor')).toBeInViewport();
|
||||
|
||||
const descriptionInputBox = '.om-block-editor[contenteditable="true"]';
|
||||
|
||||
await userPage.fill(descriptionInputBox, 'test description');
|
||||
|
||||
await userPage.getByTestId('save').click();
|
||||
|
||||
await userPage.waitForTimeout(3000);
|
||||
|
||||
const descriptionBox = '.om-block-editor[contenteditable="false"]';
|
||||
|
||||
await expect(userPage.locator(descriptionBox)).toHaveText(
|
||||
'test description'
|
||||
);
|
||||
} finally {
|
||||
await domain?.delete(apiContext);
|
||||
await user.delete(apiContext);
|
||||
}
|
||||
await afterAction();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Domains Rbac', () => {
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { uuid } from '../../utils/common';
|
||||
|
||||
type UserTeamRef = {
|
||||
@ -72,4 +73,28 @@ export class Domain {
|
||||
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async patch({
|
||||
apiContext,
|
||||
patchData,
|
||||
}: {
|
||||
apiContext: APIRequestContext;
|
||||
patchData: Operation[];
|
||||
}) {
|
||||
const response = await apiContext.patch(
|
||||
`/api/v1/domains/${this.responseData?.id}`,
|
||||
{
|
||||
data: patchData,
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.responseData = await response.json();
|
||||
|
||||
return {
|
||||
entity: this.responseData,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { Domain } from './Domain';
|
||||
|
||||
@ -76,4 +77,28 @@ export class SubDomain {
|
||||
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async patch({
|
||||
apiContext,
|
||||
patchData,
|
||||
}: {
|
||||
apiContext: APIRequestContext;
|
||||
patchData: Operation[];
|
||||
}) {
|
||||
const response = await apiContext.patch(
|
||||
`/api/v1/domains/${this.responseData?.id}`,
|
||||
{
|
||||
data: patchData,
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.responseData = await response.json();
|
||||
|
||||
return {
|
||||
entity: this.responseData,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,14 +411,9 @@ const DataProductsDetailsPage = ({
|
||||
children: (
|
||||
<DocumentationTab
|
||||
domain={dataProduct}
|
||||
editCustomAttributePermission={
|
||||
(dataProductPermission.EditAll ||
|
||||
dataProductPermission.EditCustomFields) &&
|
||||
!isVersionsView
|
||||
}
|
||||
isVersionsView={isVersionsView}
|
||||
permissions={dataProductPermission}
|
||||
type={DocumentationEntity.DATA_PRODUCT}
|
||||
viewAllPermission={dataProductPermission.ViewAll}
|
||||
onExtensionUpdate={handelExtensionUpdate}
|
||||
onUpdate={(data: Domain | DataProduct) =>
|
||||
onUpdate(data as DataProduct)
|
||||
|
||||
@ -122,7 +122,7 @@ const DomainDetailsPage = ({
|
||||
}: DomainDetailsPageProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = useForm();
|
||||
const { getEntityPermission } = usePermissionProvider();
|
||||
const { getEntityPermission, permissions } = usePermissionProvider();
|
||||
const history = useHistory();
|
||||
const { tab: activeTab, version } =
|
||||
useParams<{ tab: string; version: string }>();
|
||||
@ -202,21 +202,29 @@ const DomainDetailsPage = ({
|
||||
}, [domainPermission]);
|
||||
|
||||
const addButtonContent = [
|
||||
{
|
||||
label: t('label.asset-plural'),
|
||||
key: '1',
|
||||
onClick: () => setAssetModalVisible(true),
|
||||
},
|
||||
{
|
||||
label: t('label.sub-domain-plural'),
|
||||
key: '2',
|
||||
onClick: () => setShowAddSubDomainModal(true),
|
||||
},
|
||||
{
|
||||
label: t('label.data-product-plural'),
|
||||
key: '3',
|
||||
onClick: () => setShowAddDataProductModal(true),
|
||||
},
|
||||
...(domainPermission.Create
|
||||
? [
|
||||
{
|
||||
label: t('label.asset-plural'),
|
||||
key: '1',
|
||||
onClick: () => setAssetModalVisible(true),
|
||||
},
|
||||
{
|
||||
label: t('label.sub-domain-plural'),
|
||||
key: '2',
|
||||
onClick: () => setShowAddSubDomainModal(true),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(permissions.dataProduct.Create
|
||||
? [
|
||||
{
|
||||
label: t('label.data-product-plural'),
|
||||
key: '3',
|
||||
onClick: () => setShowAddDataProductModal(true),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
const fetchSubDomains = useCallback(async () => {
|
||||
@ -511,6 +519,7 @@ const DomainDetailsPage = ({
|
||||
<DocumentationTab
|
||||
domain={domain}
|
||||
isVersionsView={isVersionsView}
|
||||
permissions={domainPermission}
|
||||
onUpdate={(data: Domain | DataProduct) => onUpdate(data as Domain)}
|
||||
/>
|
||||
),
|
||||
@ -667,7 +676,7 @@ const DomainDetailsPage = ({
|
||||
</Col>
|
||||
<Col className="p-x-md" flex="320px">
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{!isVersionsView && domainPermission.Create && (
|
||||
{!isVersionsView && addButtonContent.length > 0 && (
|
||||
<Dropdown
|
||||
className="m-l-xs"
|
||||
data-testid="domain-details-add-button-menu"
|
||||
|
||||
@ -23,7 +23,6 @@ import DomainTypeSelectForm from '../../../../components/Domain/DomainTypeSelect
|
||||
import { DE_ACTIVE_COLOR } from '../../../../constants/constants';
|
||||
import { EntityField } from '../../../../constants/Feeds.constants';
|
||||
import { COMMON_RESIZABLE_PANEL_CONFIG } from '../../../../constants/ResizablePanel.constants';
|
||||
import { usePermissionProvider } from '../../../../context/PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../../../../context/PermissionProvider/PermissionProvider.interface';
|
||||
import { EntityType, TabSpecificField } from '../../../../enums/entity.enum';
|
||||
import { DataProduct } from '../../../../generated/entity/domains/dataProduct';
|
||||
@ -31,7 +30,6 @@ import {
|
||||
Domain,
|
||||
DomainType,
|
||||
} from '../../../../generated/entity/domains/domain';
|
||||
import { Operation } from '../../../../generated/entity/policies/policy';
|
||||
import {
|
||||
ChangeDescription,
|
||||
EntityReference,
|
||||
@ -42,7 +40,6 @@ import {
|
||||
getEntityVersionByField,
|
||||
getOwnerVersionLabel,
|
||||
} from '../../../../utils/EntityVersionUtils';
|
||||
import { checkPermission } from '../../../../utils/PermissionsUtils';
|
||||
import { CustomPropertyTable } from '../../../common/CustomPropertyTable/CustomPropertyTable';
|
||||
import FormItemLabel from '../../../common/Form/FormItemLabel';
|
||||
import ResizablePanels from '../../../common/ResizablePanels/ResizablePanels';
|
||||
@ -57,13 +54,11 @@ const DocumentationTab = ({
|
||||
domain,
|
||||
onUpdate,
|
||||
onExtensionUpdate,
|
||||
editCustomAttributePermission,
|
||||
viewAllPermission,
|
||||
isVersionsView = false,
|
||||
type = DocumentationEntity.DOMAIN,
|
||||
permissions,
|
||||
}: DocumentationTabProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const [isDescriptionEditable, setIsDescriptionEditable] =
|
||||
useState<boolean>(false);
|
||||
const [editDomainType, setEditDomainType] = useState(false);
|
||||
@ -72,40 +67,40 @@ const DocumentationTab = ({
|
||||
? ResourceEntity.DOMAIN
|
||||
: ResourceEntity.DATA_PRODUCT;
|
||||
|
||||
const { editDescriptionPermission, editOwnerPermission, editAllPermission } =
|
||||
useMemo(() => {
|
||||
if (isVersionsView) {
|
||||
return {
|
||||
editDescriptionPermission: false,
|
||||
editOwnerPermission: false,
|
||||
editAllPermission: false,
|
||||
};
|
||||
}
|
||||
|
||||
const editDescription = checkPermission(
|
||||
Operation.EditDescription,
|
||||
resourceType,
|
||||
permissions
|
||||
);
|
||||
|
||||
const editOwner = checkPermission(
|
||||
Operation.EditOwners,
|
||||
resourceType,
|
||||
permissions
|
||||
);
|
||||
|
||||
const editAll = checkPermission(
|
||||
Operation.EditAll,
|
||||
resourceType,
|
||||
permissions
|
||||
);
|
||||
|
||||
const {
|
||||
editDescriptionPermission,
|
||||
editOwnerPermission,
|
||||
editAllPermission,
|
||||
editCustomAttributePermission,
|
||||
viewAllPermission,
|
||||
} = useMemo(() => {
|
||||
if (isVersionsView) {
|
||||
return {
|
||||
editDescriptionPermission: editDescription || editAll,
|
||||
editOwnerPermission: editOwner || editAll,
|
||||
editAllPermission: editAll,
|
||||
editDescriptionPermission: false,
|
||||
editOwnerPermission: false,
|
||||
editAllPermission: false,
|
||||
editCustomAttributePermission: false,
|
||||
};
|
||||
}, [permissions, isVersionsView, resourceType]);
|
||||
}
|
||||
|
||||
const editDescription = permissions?.EditDescription;
|
||||
|
||||
const editOwner = permissions?.EditOwners;
|
||||
|
||||
const editAll = permissions?.EditAll;
|
||||
|
||||
const editCustomAttribute = permissions?.EditCustomFields;
|
||||
|
||||
const viewAll = permissions?.ViewAll;
|
||||
|
||||
return {
|
||||
editDescriptionPermission: editAll || editDescription,
|
||||
editOwnerPermission: editAll || editOwner,
|
||||
editAllPermission: editAll,
|
||||
editCustomAttributePermission: editAll || editCustomAttribute,
|
||||
viewAllPermission: viewAll,
|
||||
};
|
||||
}, [permissions, isVersionsView, resourceType]);
|
||||
|
||||
const description = useMemo(
|
||||
() =>
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface';
|
||||
import { DataProduct } from '../../../../generated/entity/domains/dataProduct';
|
||||
import { Domain } from '../../../../generated/entity/domains/domain';
|
||||
|
||||
@ -19,8 +20,7 @@ export interface DocumentationTabProps {
|
||||
isVersionsView?: boolean;
|
||||
type?: DocumentationEntity;
|
||||
onExtensionUpdate?: (updatedDataProduct: DataProduct) => Promise<void>;
|
||||
editCustomAttributePermission?: boolean;
|
||||
viewAllPermission?: boolean;
|
||||
permissions?: OperationPermission;
|
||||
}
|
||||
|
||||
export enum DocumentationEntity {
|
||||
|
||||
@ -14,6 +14,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MOCK_DOMAIN } from '../../../../mocks/Domains.mock';
|
||||
import { MOCK_PERMISSIONS } from '../../../../mocks/Glossary.mock';
|
||||
import DocumentationTab from './DocumentationTab.component';
|
||||
|
||||
// Mock the onUpdate function
|
||||
@ -23,6 +24,7 @@ const defaultProps = {
|
||||
domain: MOCK_DOMAIN,
|
||||
onUpdate: mockOnUpdate,
|
||||
isVersionsView: false,
|
||||
permissions: MOCK_PERMISSIONS,
|
||||
};
|
||||
|
||||
jest.mock('../../../common/EntityDescription/DescriptionV1', () => {
|
||||
@ -33,10 +35,6 @@ jest.mock('../../../common/ProfilePicture/ProfilePicture', () =>
|
||||
jest.fn().mockReturnValue(<>ProfilePicture</>)
|
||||
);
|
||||
|
||||
jest.mock('../../../../utils/PermissionsUtils', () => ({
|
||||
checkPermission: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
describe('DocumentationTab', () => {
|
||||
it('should render the initial content', () => {
|
||||
const { getByTestId } = render(<DocumentationTab {...defaultProps} />, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user