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:
Shailesh Parmar 2025-07-13 18:36:27 +05:30
parent af39ce3515
commit 1373b0456a
5 changed files with 150 additions and 98 deletions

View File

@ -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')
);

View File

@ -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

View File

@ -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)) {

View File

@ -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();
});

View File

@ -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>
);