Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion openmetadata-ui/src/main/resources/ui/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ export default [
// TypeScript rules
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-use-before-define': 'warn',
// Cleared to zero and locked by the ESLint-cleanup stack — safe reorders
// where possible, documented disables for mutual-recursion / derived-below
// cases. Promoted to error so CI blocks any regression.
'@typescript-eslint/no-use-before-define': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': [
'error',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import { TestDefinition } from '../../../../generated/tests/testDefinition';
import { TestSuite } from '../../../../generated/tests/testSuite';
import { TableSearchSource } from '../../../../interface/search.interface';
import testCaseClassBase from '../../../../pages/IncidentManager/IncidentManagerDetailPage/TestCaseClassBase';

Check warning on line 39 in openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormDrawer.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Pages are route-level composition modules. Move the shared implementation/type to a lower layer instead of importing a page from here
import {
addIngestionPipeline,
deployIngestionPipelineById,
Expand Down Expand Up @@ -90,7 +90,7 @@
testCase,
showOnlyParameter = false,
onUpdate,
}: TestCaseFormDrawerProps) => {

Check warning on line 93 in openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormDrawer.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 17 which is greater than 10 authorized.","cost":7,"secondaryLocations":[{"line":93,"column":28,"endLine":93,"endColumn":30,"message":"+1"},{"line":107,"column":27,"endLine":107,"endColumn":29,"message":"+1"},{"line":311,"column":10,"endLine":311,"endColumn":11,"message":"+1"},{"line":339,"column":32,"endLine":339,"endColumn":33,"message":"+1"},{"line":348,"column":30,"endLine":348,"endColumn":32,"message":"+1"},{"line":348,"column":47,"endLine":348,"endColumn":48,"message":"+1"},{"line":362,"column":23,"endLine":362,"endColumn":25,"message":"+1"},{"line":362,"column":15,"endLine":362,"endColumn":17,"message":"+1"},{"line":364,"column":41,"endLine":364,"endColumn":42,"message":"+1"},{"line":384,"column":23,"endLine":384,"endColumn":24,"message":"+1"},{"line":405,"column":4,"endLine":405,"endColumn":5,"message":"+1"},{"line":411,"column":19,"endLine":411,"endColumn":21,"message":"+1"},{"line":416,"column":30,"endLine":416,"endColumn":31,"message":"+1"},{"line":418,"column":56,"endLine":418,"endColumn":58,"message":"+1"},{"line":437,"column":2,"endLine":437,"endColumn":4,"message":"+1"},{"line":444,"column":32,"endLine":444,"endColumn":33,"message":"+1"},{"line":446,"column":21,"endLine":446,"endColumn":23,"message":"+1"}]}
const { t } = useTranslation();
const { getResourceLimit } = useLimitStore();
const { isAirflowAvailable } = useAirflowStatus();
Expand Down Expand Up @@ -187,7 +187,7 @@

const ingestion = await addIngestionPipeline(pipeline);
if (isAirflowAvailable && ingestionPipeline.EditAll) {
await deployIngestionPipelineById(ingestion.id ?? '');

Check warning on line 190 in openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormDrawer.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
}
},
[formContext, testSuite, table, isAirflowAvailable, ingestionPipeline]
Expand Down Expand Up @@ -238,7 +238,7 @@
jsonPatch
);
showSuccessToast(
t('server.update-entity-success', { entity: t('label.test-case') })

Check warning on line 241 in openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormDrawer.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Define a constant instead of duplicating this literal 4 times
);
onUpdate?.(updated);
},
Expand Down Expand Up @@ -369,6 +369,8 @@
testCaseFormBody
);

const closeDrawerRef = useRef<() => void>(() => undefined);

const formBody = (
<HookForm
form={form}
Expand All @@ -387,8 +389,6 @@
</HookForm>
);

const closeDrawerRef = useRef<() => void>(() => undefined);

// Every dismissal path (cancel, X, Escape, backdrop, programmatic close)
// funnels through the base drawer's onClose, so the parent is notified
// exactly once and the form resets. The backdrop stays dismissable to match
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
} from '../../../utils/StringUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import { withActivityFeed } from '../../AppRouter/withActivityFeed';
import { useFormDrawerWithHook } from '../../common/atoms/drawer';

Check warning on line 118 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Import the internal module directly instead of its index barrel so unrelated siblings do not enter the bundle graph
import { CoverImage } from '../../common/CoverImage/CoverImage.component';
import DeleteModal from '../../common/DeleteModal/DeleteModal';
import AnnouncementCard from '../../common/EntityPageInfos/AnnouncementCard/AnnouncementCard';
Expand Down Expand Up @@ -153,7 +153,7 @@
onNavigate,
refreshDomains,
isTreeView = false,
}: DomainDetailsProps) => {

Check warning on line 156 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 29 which is greater than 10 authorized.","cost":19,"secondaryLocations":[{"line":156,"column":23,"endLine":156,"endColumn":25,"message":"+1"},{"line":161,"column":78,"endLine":161,"endColumn":80,"message":"+1"},{"line":227,"column":64,"endLine":227,"endColumn":66,"message":"+1"},{"line":230,"column":41,"endLine":230,"endColumn":43,"message":"+1"},{"line":668,"column":6,"endLine":668,"endColumn":7,"message":"+1"},{"line":682,"column":6,"endLine":682,"endColumn":7,"message":"+1"},{"line":681,"column":16,"endLine":681,"endColumn":18,"message":"+1"},{"line":801,"column":6,"endLine":801,"endColumn":7,"message":"+1"},{"line":821,"column":6,"endLine":821,"endColumn":7,"message":"+1"},{"line":843,"column":6,"endLine":843,"endColumn":7,"message":"+1"},{"line":865,"column":6,"endLine":865,"endColumn":7,"message":"+1"},{"line":984,"column":2,"endLine":984,"endColumn":4,"message":"+1"},{"line":994,"column":21,"endLine":994,"endColumn":23,"message":"+1"},{"line":1017,"column":28,"endLine":1017,"endColumn":30,"message":"+1"},{"line":1030,"column":60,"endLine":1030,"endColumn":62,"message":"+1"},{"line":1030,"column":29,"endLine":1030,"endColumn":31,"message":"+1"},{"line":1048,"column":28,"endLine":1048,"endColumn":30,"message":"+1"},{"line":1056,"column":31,"endLine":1056,"endColumn":33,"message":"+1"},{"line":1061,"column":24,"endLine":1061,"endColumn":25,"message":"+1"},{"line":1082,"column":65,"endLine":1082,"endColumn":67,"message":"+1"},{"line":1082,"column":31,"endLine":1082,"endColumn":33,"message":"+1"},{"line":1112,"column":32,"endLine":1112,"endColumn":34,"message":"+1"},{"line":1132,"column":39,"endLine":1132,"endColumn":40,"message":"+1"},{"line":1140,"column":40,"endLine":1140,"endColumn":42,"message":"+1"},{"line":1142,"column":47,"endLine":1142,"endColumn":48,"message":"+1"},{"line":1144,"column":38,"endLine":1144,"endColumn":39,"message":"+1"},{"line":1171,"column":14,"endLine":1171,"endColumn":16,"message":"+1"},{"line":1176,"column":59,"endLine":1176,"endColumn":61,"message":"+1"},{"line":1205,"column":45,"endLine":1205,"endColumn":47,"message":"+1"}]}
const { t } = useTranslation();
const { isMarketplace } = useMarketplaceStore();
const location = useLocation();
Expand Down Expand Up @@ -260,7 +260,7 @@
setAssetCount(0);
showErrorToast(
error as AxiosError,
t('server.entity-fetch-error', {

Check warning on line 263 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Define a constant instead of duplicating this literal 3 times
entity: t('label.asset-plural-lowercase'),
})
);
Expand Down Expand Up @@ -319,13 +319,14 @@
);
}
}
}, [isVersionsView, encodedFqn]);

Check warning on line 322 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

React Hook useCallback has missing dependencies: 'domain.fullyQualifiedName' and 't'. Either include them or remove the dependency array

const handleTabChange = (activeKey: string) => {

Check warning on line 324 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

The 'handleTabChange' function makes the dependencies of useCallback Hook (at line 606) change on every render. To fix this, wrap the definition of 'handleTabChange' in its own useCallback() Hook

Check warning on line 324 in openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetails/DomainDetails.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

The 'handleTabChange' function makes the dependencies of useCallback Hook (at line 433) change on every render. To fix this, wrap the definition of 'handleTabChange' in its own useCallback() Hook
if (activeKey === EntityTabs.ASSETS) {
// refresh domain count when assets tab is selected
fetchDomainAssets();
}
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- derived below from `tabs`
if (activeKey !== activeTab) {
if (onActiveTabChange) {
onActiveTabChange(activeKey as EntityTabs);
Expand Down Expand Up @@ -765,6 +766,7 @@
const newFqn = domain.parent
? `${domain.parent.fullyQualifiedName}.${newName.trim()}`
: newName.trim();
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- derived below from `tabs`
navigate(getDomainDetailsPath(newFqn, activeTab));
}
} catch {
Expand Down Expand Up @@ -908,6 +910,7 @@
handleAssetSave: () => {
fetchDomainAssets();
assetTabRef.current?.refreshAssets();
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- derived below from `tabs`
activeTab !== EntityTabs.ASSETS && handleTabChange(EntityTabs.ASSETS);
},
setShowAddSubDomainModal: openSubDomainDrawer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const DomainTreeView = ({
const firstDomain = selectDomain(domains, resetExpandedItems, domainFqn);

if ((firstDomain?.childrenCount || 0) > 0 && shouldLoadChildren) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- useCallback defined below
loadDomains(firstDomain.fullyQualifiedName as string);
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ const AuditLogsPage = () => {
}

if (!cancelled) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutually recursive with pollOnce
scheduleNextPoll();
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,36 @@ const VECTOR_INDEXABLE_ENTITIES = new Set([
'topic',
]);

/**
* Format avg stage latency as a short human string. Returns "—" when no records or no time
* has been recorded yet (e.g. fresh job, or stages that haven't reported timing because the
* legacy non-distributed path is in use). Below 1 ms shows "<1 ms" rather than rounding to 0.
*/
export const formatLatencyAverage = (
totalTimeMs?: number,
successRecords?: number
): string => {
// No timing recorded yet (legacy non-distributed path) or no records to divide by.
// totalTimeMs === 0 with successRecords > 0 is a valid sub-millisecond batch and
// falls through to the "<1 ms" branch.
if (
totalTimeMs === undefined ||
successRecords === undefined ||
successRecords <= 0
) {
return '—';
}
const avgMs = totalTimeMs / successRecords;
if (avgMs < 1) {
return '<1 ms';
}
if (avgMs < 1000) {
return `${avgMs.toFixed(1)} ms`;
}

return `${(avgMs / 1000).toFixed(2)} s`;
};

export const getEntityStatsData = (data: {
[key: string]: StepStats;
}): EntityStatsData[] => {
Expand Down Expand Up @@ -144,36 +174,6 @@ export const getEntityStatsData = (data: {
);
};

/**
* Format avg stage latency as a short human string. Returns "—" when no records or no time
* has been recorded yet (e.g. fresh job, or stages that haven't reported timing because the
* legacy non-distributed path is in use). Below 1 ms shows "<1 ms" rather than rounding to 0.
*/
export const formatLatencyAverage = (
totalTimeMs?: number,
successRecords?: number
): string => {
// No timing recorded yet (legacy non-distributed path) or no records to divide by.
// totalTimeMs === 0 with successRecords > 0 is a valid sub-millisecond batch and
// falls through to the "<1 ms" branch.
if (
totalTimeMs === undefined ||
successRecords === undefined ||
successRecords <= 0
) {
return '—';
}
const avgMs = totalTimeMs / successRecords;
if (avgMs < 1) {
return '<1 ms';
}
if (avgMs < 1000) {
return `${avgMs.toFixed(1)} ms`;
}

return `${(avgMs / 1000).toFixed(2)} s`;
};

/**
* Format throughput in records per second derived from the same total-time / success-records
* pair. Useful as a secondary signal next to avg latency when comparing entities or runs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ export const getCronDefaultValue = (appName: string) => {
return initialValue;
};

export const getDefaultScheduleFromPeriod = (
includePeriodOptions: string[]
) => {
if (includePeriodOptions.includes('day')) {
return DEFAULT_SCHEDULE_CRON_DAILY;
} else if (includePeriodOptions.includes('week')) {
return DEFAULT_SCHEDULE_CRON_WEEKLY;
} else if (includePeriodOptions.includes('month')) {
return DEFAULT_SCHEDULE_CRON_MONTHLY;
} else if (includePeriodOptions.includes('hour')) {
return DEFAULT_SCHEDULE_CRON_HOURLY;
}

return DEFAULT_SCHEDULE_CRON_DAILY;
};

export const getDefaultScheduleValue = ({
defaultSchedule,
includePeriodOptions,
Expand All @@ -182,22 +198,6 @@ export const getDefaultScheduleValue = ({
return getDefaultScheduleFromPeriod(includePeriodOptions);
};

export const getDefaultScheduleFromPeriod = (
includePeriodOptions: string[]
) => {
if (includePeriodOptions.includes('day')) {
return DEFAULT_SCHEDULE_CRON_DAILY;
} else if (includePeriodOptions.includes('week')) {
return DEFAULT_SCHEDULE_CRON_WEEKLY;
} else if (includePeriodOptions.includes('month')) {
return DEFAULT_SCHEDULE_CRON_MONTHLY;
} else if (includePeriodOptions.includes('hour')) {
return DEFAULT_SCHEDULE_CRON_HOURLY;
}

return DEFAULT_SCHEDULE_CRON_DAILY;
};

export const getUpdatedStateFromFormState = <T>(
currentState: StateValue,
formValues: StateValue & WorkflowExtraConfig & T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ const serializeTimeInterval = (
return isEmptyExtensionValue(interval) ? undefined : interval;
};

export const hasPopulatedTableRows = (value: unknown) =>
isRecord(value) &&
Array.isArray(value.rows) &&
value.rows.some((row) => isRecord(row) && Object.values(row).some(Boolean));

const serializeTableValue = (raw: unknown): unknown =>
hasPopulatedTableRows(raw) ? raw : undefined;

Expand Down Expand Up @@ -217,11 +222,6 @@ export const filterPopulatedTableRows = <T extends Record<string, unknown>>(
rows: T[]
) => rows.filter((row) => Object.values(row).some(Boolean));

export const hasPopulatedTableRows = (value: unknown) =>
isRecord(value) &&
Array.isArray(value.rows) &&
value.rows.some((row) => isRecord(row) && Object.values(row).some(Boolean));

export const getCustomPropertyEntityPathname = (entityType: string) => {
const entityPathEntries = Object.entries(ENTITY_PATH);
const entityPath = entityPathEntries.find(([, path]) => path === entityType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ const WorkflowHistory = withSuspenseFallback(
)
);

const GlossaryTermDomainWidget = () => {
const { entityRules } = useGenericContext();

return (
<DomainLabelV2
showDomainHeading
multiple={entityRules?.canAddMultipleDomains ?? true}
/>
);
};

export const getGlossaryTermWidgetFromKey = (widgetConfig: WidgetConfig) => {
if (
widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.WORKFLOW_HISTORY)
Expand Down Expand Up @@ -121,14 +132,3 @@ export const getGlossaryTermWidgetFromKey = (widgetConfig: WidgetConfig) => {
/>
);
};

const GlossaryTermDomainWidget = () => {
const { entityRules } = useGenericContext();

return (
<DomainLabelV2
showDomainHeading
multiple={entityRules?.canAddMultipleDomains ?? true}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export const getKeyValues = ({
}

// Handle special service configurations
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutually recursive with getKeyValues
const specialConfig = handleSpecialServiceConfig(
serviceType,
key,
Expand All @@ -183,6 +184,7 @@ export const getKeyValues = ({
serviceType === EntityType.DATABASE_SERVICE &&
key === 'configSource'
) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutually recursive with getKeyValues
const configSource = handleDatabaseConfigSource(
key,
value,
Expand Down
Loading