mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-26 09:22:14 +00:00
Enhance Permission Handling in Data Quality Components
- Integrated permission checks for creating and editing test cases in TestCaseFormV1 and BundleSuiteForm. - Updated state management to reflect permission-based conditions for enabling features like pipeline creation and scheduler options. - Refactored related components to ensure proper dependency handling in hooks. - Improved test coverage for permission-related functionalities across affected components.
This commit is contained in:
parent
af39ce3515
commit
1373b0456a
@ -137,6 +137,25 @@ jest.mock('../../../../context/LimitsProvider/useLimitsStore', () => ({
|
||||
useLimitStore: () => ({ getResourceLimit: () => 100 }),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../context/PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: () => ({
|
||||
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
|
||||
EditAll: true,
|
||||
EditTests: true,
|
||||
}),
|
||||
permissions: {
|
||||
ingestionPipeline: {
|
||||
Create: true,
|
||||
EditAll: true,
|
||||
},
|
||||
testCase: {
|
||||
Create: true,
|
||||
EditAll: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('crypto-random-string-with-promisify-polyfill', () =>
|
||||
jest.fn().mockReturnValue('ABC123')
|
||||
);
|
||||
|
@ -147,7 +147,7 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
const [form] = useForm<FormValues>();
|
||||
const { isAirflowAvailable } = useAirflowStatus();
|
||||
const { getEntityPermissionByFqn, permissions } = usePermissionProvider();
|
||||
const { ingestionPipeline } = permissions;
|
||||
const { ingestionPipeline, testCase } = permissions;
|
||||
|
||||
const TEST_LEVEL_OPTIONS: SelectionOption[] = [
|
||||
{
|
||||
@ -457,7 +457,8 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
// HOOKS - Callbacks (grouped by functionality)
|
||||
// =============================================
|
||||
// Pipeline-related callbacks
|
||||
const checkExistingPipelines = useCallback(async (testSuiteFqn: string) => {
|
||||
const checkExistingPipelines = useCallback(
|
||||
async (testSuiteFqn: string) => {
|
||||
try {
|
||||
const { paging } = await getIngestionPipelines({
|
||||
testSuite: testSuiteFqn,
|
||||
@ -469,7 +470,9 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
} catch (error) {
|
||||
setCanCreatePipeline(true);
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[ingestionPipeline]
|
||||
);
|
||||
|
||||
// Form interaction callbacks
|
||||
const handleCancel = useCallback(() => {
|
||||
@ -647,8 +650,11 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
// Permission checking callback
|
||||
const checkTablePermissions = useCallback(
|
||||
async (tableFqn: string) => {
|
||||
setIsCheckingPermissions(true);
|
||||
if (testCase.Create) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
setIsCheckingPermissions(true);
|
||||
try {
|
||||
const permissions = await getEntityPermissionByFqn(
|
||||
ResourceEntity.TABLE,
|
||||
@ -656,6 +662,8 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
);
|
||||
const canCreate = permissions.EditAll || permissions.EditTests;
|
||||
|
||||
setCanCreatePipeline(canCreate);
|
||||
|
||||
if (!canCreate) {
|
||||
// Return false to trigger validation error
|
||||
return Promise.reject(
|
||||
@ -884,24 +892,31 @@ const TestCaseFormV1: FC<TestCaseFormV1Props> = ({
|
||||
|
||||
// Check for existing pipelines when table is selected
|
||||
useEffect(() => {
|
||||
if (selectedTableData?.testSuite?.fullyQualifiedName) {
|
||||
// Table has test suite - check for existing pipelines
|
||||
checkExistingPipelines(selectedTableData.testSuite.fullyQualifiedName);
|
||||
} else if (testSuite?.fullyQualifiedName) {
|
||||
// Using provided test suite - check for existing pipelines
|
||||
checkExistingPipelines(testSuite.fullyQualifiedName);
|
||||
} else if (selectedTable) {
|
||||
// Table selected but no test suite - can create pipeline (test suite will be created)
|
||||
setCanCreatePipeline(true);
|
||||
} else {
|
||||
// No table selected - hide scheduler
|
||||
// Early return if user doesn't have permission to create pipelines or Early return if no table is selected
|
||||
if (!ingestionPipeline.Create || !selectedTable) {
|
||||
setCanCreatePipeline(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the test suite FQN from either the table data or provided test suite
|
||||
const testSuiteFqn =
|
||||
selectedTableData?.testSuite?.fullyQualifiedName ||
|
||||
testSuite?.fullyQualifiedName;
|
||||
|
||||
if (testSuiteFqn) {
|
||||
// Check for existing pipelines if we have a test suite
|
||||
checkExistingPipelines(testSuiteFqn);
|
||||
} else {
|
||||
// No test suite exists - can create pipeline (test suite will be created)
|
||||
setCanCreatePipeline(true);
|
||||
}
|
||||
}, [
|
||||
selectedTableData?.testSuite?.fullyQualifiedName,
|
||||
testSuite?.fullyQualifiedName,
|
||||
selectedTable,
|
||||
checkExistingPipelines,
|
||||
ingestionPipeline,
|
||||
]);
|
||||
|
||||
// Initialize manual edit flag
|
||||
|
@ -100,7 +100,7 @@ export const AddTestCaseList = ({
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[selectedTest, filters, testCaseParams]
|
||||
[selectedTest]
|
||||
);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
@ -123,7 +123,7 @@ export const AddTestCaseList = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
[searchTerm, totalCount, items, isLoading, pageNumber, fetchTestCases]
|
||||
[searchTerm, totalCount, items, isLoading]
|
||||
);
|
||||
|
||||
const handleCardClick = (details: TestCase) => {
|
||||
@ -163,7 +163,7 @@ export const AddTestCaseList = ({
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchTestCases({ searchText: searchTerm });
|
||||
}, [searchTerm, fetchTestCases]);
|
||||
}, [searchTerm]);
|
||||
|
||||
const renderList = useMemo(() => {
|
||||
if (!isLoading && isEmpty(items)) {
|
||||
|
@ -97,6 +97,17 @@ jest.mock('../../../context/LimitsProvider/useLimitsStore', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../context/PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: () => ({
|
||||
permissions: {
|
||||
ingestionPipeline: {
|
||||
Create: true,
|
||||
EditAll: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../hooks/useApplicationStore', () => ({
|
||||
useApplicationStore: jest.fn().mockReturnValue({
|
||||
currentUser: {
|
||||
@ -208,7 +219,9 @@ describe('BundleSuiteForm Component', () => {
|
||||
it('should render form in drawer mode', async () => {
|
||||
render(<BundleSuiteForm {...mockProps} />);
|
||||
|
||||
expect(await screen.findAllByText('label.create-entity')).toHaveLength(2); // One in header, one in card title
|
||||
expect(
|
||||
await screen.findByText('label.create-entity')
|
||||
).toBeInTheDocument(); // Should appear in card title
|
||||
expect(document.querySelector('.bundle-suite-form')).toBeInTheDocument();
|
||||
expect(document.querySelector('.drawer-mode')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -34,6 +34,7 @@ import { MAX_NAME_LENGTH } from '../../../constants/constants';
|
||||
import { DEFAULT_SCHEDULE_CRON_DAILY } from '../../../constants/Schedular.constants';
|
||||
import { useAirflowStatus } from '../../../context/AirflowStatusProvider/AirflowStatusProvider';
|
||||
import { useLimitStore } from '../../../context/LimitsProvider/useLimitsStore';
|
||||
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
|
||||
import { OwnerType } from '../../../enums/user.enum';
|
||||
import {
|
||||
ConfigType,
|
||||
@ -92,6 +93,8 @@ const BundleSuiteForm: React.FC<BundleSuiteFormProps> = ({
|
||||
const { config } = useLimitStore();
|
||||
const { currentUser } = useApplicationStore();
|
||||
const { isAirflowAvailable } = useAirflowStatus();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const { ingestionPipeline } = permissions;
|
||||
const enableScheduler = Form.useWatch('enableScheduler', form);
|
||||
|
||||
// =============================================
|
||||
@ -266,7 +269,7 @@ const BundleSuiteForm: React.FC<BundleSuiteFormProps> = ({
|
||||
testSuiteId: testSuite.id ?? '',
|
||||
});
|
||||
|
||||
if (formData.enableScheduler) {
|
||||
if (formData.enableScheduler && ingestionPipeline.Create) {
|
||||
await createAndDeployPipeline(testSuite, formData);
|
||||
}
|
||||
|
||||
@ -391,6 +394,7 @@ const BundleSuiteForm: React.FC<BundleSuiteFormProps> = ({
|
||||
</Card>
|
||||
|
||||
{/* Scheduler with Toggle */}
|
||||
{ingestionPipeline.Create && (
|
||||
<Card className="form-card-section" data-testid="scheduler-card">
|
||||
<div className="card-title-container d-flex items-center gap-3 m-b-lg">
|
||||
<Form.Item
|
||||
@ -460,6 +464,7 @@ const BundleSuiteForm: React.FC<BundleSuiteFormProps> = ({
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user