Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.openmetadata.service.drive;

import static org.openmetadata.service.Entity.ADMIN_USER_NAME;
import static org.openmetadata.service.jdbi3.ContextFileContentRepository.CONTEXT_FILE_CONTENT_ENTITY;
import static org.openmetadata.service.jdbi3.ContextFileRepository.CONTEXT_FILE_ENTITY;

Expand Down Expand Up @@ -299,7 +298,7 @@ private boolean updateFile(UUID fileId, Function<ContextFile, ContextFile> updat
return false;
}
try {
repository.updateIfCurrent(null, current, updated, ADMIN_USER_NAME);
repository.updateIfCurrent(null, current, updated, current.getUpdatedBy());
return true;
} catch (PreconditionFailedException e) {
LOG.debug("Context file {} changed during extraction update", fileId);
Expand All @@ -325,7 +324,9 @@ private boolean updateContent(
return false;
}
try {
repository.getContentRepository().updateIfCurrent(null, current, updated, ADMIN_USER_NAME);
repository
.getContentRepository()
.updateIfCurrent(null, current, updated, current.getUpdatedBy());
return true;
} catch (PreconditionFailedException e) {
LOG.debug("Context file content {} changed during extraction update", contentId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
@ExtendWith(MockitoExtension.class)
class ContextFileExtractionServiceTest {

private static final String UPLOADER = "test.user";

@Mock private ContextFileRepository repository;
@Mock private ContextFileContentRepository contentRepository;
@Mock private AssetRepository assetRepository;
Expand Down Expand Up @@ -78,15 +80,17 @@ void setUp() {
.withFileType(ContextFileType.PDF)
.withFileExtension("pdf")
.withHeadContentId(contentId.toString())
.withProcessingStatus(ProcessingStatus.Uploaded);
.withProcessingStatus(ProcessingStatus.Uploaded)
.withUpdatedBy(UPLOADER);

content =
new ContextFileContent()
.withId(contentId)
.withName("v1")
.withAssetId("asset-1")
.withContextFile(file.getEntityReference())
.withProcessingStatus(ProcessingStatus.Uploaded);
.withProcessingStatus(ProcessingStatus.Uploaded)
.withUpdatedBy(UPLOADER);

asset = new Asset();
asset.setId("asset-1");
Expand All @@ -112,9 +116,9 @@ void processSuccessMarksAnalyzingThenProcessed() throws Exception {
service(Runnable::run, () -> assetService).process(fileId, contentId);

verify(repository, times(2))
.updateIfCurrent(isNull(), same(file), updatedFileCaptor.capture(), anyString());
.updateIfCurrent(isNull(), same(file), updatedFileCaptor.capture(), eq(UPLOADER));
verify(contentRepository, times(2))
.updateIfCurrent(isNull(), same(content), updatedContentCaptor.capture(), anyString());
.updateIfCurrent(isNull(), same(content), updatedContentCaptor.capture(), eq(UPLOADER));

List<ContextFile> fileUpdates = updatedFileCaptor.getAllValues();
assertEquals(ProcessingStatus.Analyzing, fileUpdates.get(0).getProcessingStatus());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ test.describe('Context Center - Dashboard', () => {
.catch(() => false);
if (isStillInTopThree) {
await expect(seededDocument).toBeVisible();
await seededDocument.click();
await expect(page).toHaveURL(/\/context-center\/documents\?document=/);
}
});

Expand Down Expand Up @@ -156,6 +158,8 @@ test.describe('Context Center - Dashboard', () => {
.catch(() => false);
if (isStillInTopThree) {
await expect(seededMemory).toBeVisible();
await seededMemory.click();
await expect(page).toHaveURL(/\/context-center\/memories\?memory=/);
}
});
});
Expand Down Expand Up @@ -184,7 +188,13 @@ test.describe('Context Center - Dashboard', () => {

const recentlyViewedCard = page.getByTestId('recently-viewed-card');
await expect(recentlyViewedCard).toBeVisible();
await expect(recentlyViewedCard.getByText(displayName)).toBeVisible();

const recentlyViewedItem = recentlyViewedCard.getByText(displayName);
await expect(recentlyViewedItem).toBeVisible();
await recentlyViewedItem.click();
await expect(page).toHaveURL(
new RegExp(`/context-center/articles/${article.fullyQualifiedName}`)
);
});
});

Expand Down Expand Up @@ -227,6 +237,10 @@ test.describe('Context Center - Dashboard', () => {

const firstItem = mostCitedCard.getByTestId('most-cited-count').first();
await expect(firstItem).toContainText('Cited 999999 times');

const firstItemRow = mostCitedCard.getByRole('button').first();
await firstItemRow.click();
await expect(page).toHaveURL(/\/context-center\/memories\?memory=/);
});
});

Expand Down Expand Up @@ -286,6 +300,15 @@ test.describe('Context Center - Dashboard', () => {

const childRow = tree.getByRole('row', { name: fileName });
await expect(childRow).toBeVisible();

await childRow.getByRole('button', { name: fileName }).click();
await expect(page).toHaveURL(
new RegExp(`/context-center/documents\\?document=${file.id}`)
);

const panel = page.getByTestId('document-preview-panel');
await expect(panel).toBeVisible();
await expect(panel.getByTestId('preview-file-name')).toHaveText(fileName);
});
});

Expand Down Expand Up @@ -374,19 +397,29 @@ test.describe('Context Center - Dashboard', () => {

await test.step('Articles card redirects to /context-center/articles', async () => {
await navigateToDashboard(page);
await page.getByTestId('article-detail-card').click();
await page
.getByTestId('article-detail-card')
.getByRole('button', { name: 'View All Articles' })
.click();
await expect(page).toHaveURL(/\/context-center\/articles/);
});

await test.step('Documents card redirects to /context-center/documents', async () => {
await navigateToDashboard(page);
await page.getByTestId('document-detail-card').click();
await page
.getByTestId('document-detail-card')
.getByRole('button', { name: 'View All Documents' })
.first()
.click();
await expect(page).toHaveURL(/\/context-center\/documents/);
});

await test.step('Memories card redirects to /context-center/memories', async () => {
await navigateToDashboard(page);
await page.getByTestId('memory-detail-card').click();
await page
.getByTestId('memory-detail-card')
.getByRole('button', { name: 'View All Memories' })
.click();
await expect(page).toHaveURL(/\/context-center\/memories/);
await waitForAllLoadersToDisappear(page);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
navigateToMemories,
scrollHierarchyToNode,
scrollListingToCard,
searchAndGetDocumentRow,
searchAndGetMemoryRow,
uploadDisposableDocument,
waitForDocumentInArchive,
Expand Down Expand Up @@ -1240,6 +1241,19 @@ test.describe('Context Center Permissions', () => {
const { apiContext, afterAction } = await getDefaultAdminAPIContext(
browser
);

await waitForDocumentProcessingComplete(apiContext, uploadedData.id);

await createAllPage.reload();
await waitForAllLoadersToDisappear(createAllPage);
await navigateToDocuments(createAllPage);

const row = await searchAndGetDocumentRow(createAllPage, fileName);
await expect(row).toBeVisible();
await expect(row.getByTestId('document-updated-by')).toHaveText(
createAllUser.responseData.name
);

await apiContext
.delete(
`/api/v1/contextCenter/drive/files/${uploadedData.id}?hardDelete=true`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
return (
<Box
align="center"
className="tw:px-4 tw:py-3 tw:border-b tw:border-secondary tw:last:border-0"
className="tw:px-4 tw:py-3 tw:border-b tw:border-secondary"
data-testid={`archive-row-${item.id}`}
gap={4}>
<FileIcon
Expand Down Expand Up @@ -152,7 +152,7 @@
return (
<Card className="tw:flex tw:flex-col">
{Array.from({ length: 8 }).map((_, idx) => (
<ArchiveRowSkeleton key={idx} />

Check warning on line 155 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/ArchiveView/ArchiveView.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Do not use Array index in keys
))}
</Card>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
} from '@openmetadata/ui-core-components';
import { ArrowNarrowRight } from '@untitledui/icons';
import classNames from 'classnames';
import { FC, Fragment } from 'react';
import { FC, Fragment, KeyboardEvent, MouseEvent } from 'react';
import {
ContextKnowledgePillarCardProps,
PillarRecentItem,
Expand All @@ -35,8 +35,28 @@
readonly Icon: FC<{ className?: string }>;
readonly item: PillarRecentItem;
}) {
const handleClick = (e: MouseEvent) => {
e.stopPropagation();
item.onClick();
};

const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
e.stopPropagation();
item.onClick();
}
Comment thread
gitar-bot[bot] marked this conversation as resolved.
};

return (
<Box align="center" className="tw:py-1.5" gap={2}>
<Box
Comment thread
gitar-bot[bot] marked this conversation as resolved.
align="center"
className="tw:py-1.5 tw:cursor-pointer tw:rounded tw:hover:bg-primary_hover"
gap={2}
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={handleKeyDown}>
{item.icon ? (
item.icon
) : (
Expand All @@ -58,7 +78,7 @@
</div>
<Box align="center" gap={1}>
{item.meta.map((metaItem, index) => (
<Fragment key={`${index}-${metaItem}`}>

Check warning on line 81 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/ContextKnowledgePillarCard/ContextKnowledgePillarCard.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Do not use Array index in keys
<div className="tw:max-w-20">
<Typography
ellipsis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface PillarRecentItem {
title: string;
meta: string[];
icon?: ReactElement;
onClick: () => void;
}

export interface ContextKnowledgePillarCardProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2026 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { fireEvent, render, screen } from '@testing-library/react';
import { ReactComponent as FileIcon } from '../../../assets/svg/common/file.svg';
import ContextKnowledgePillarCard from './ContextKnowledgePillarCard.component';
import { PillarRecentItem } from './ContextKnowledgePillarCard.interface';

jest.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));

const baseProps = {
cta: 'View all',
icon: FileIcon,
stat: '5',
statSub: 'items',
subtitle: 'subtitle',
title: 'Articles',
};

describe('ContextKnowledgePillarCard', () => {
it('calls onClick when the card body is clicked', () => {
const onClick = jest.fn();
render(
<ContextKnowledgePillarCard
{...baseProps}
dataTestId="article-detail-card"
recent={[{ meta: [], onClick: jest.fn(), title: 'Item 1' }]}
onClick={onClick}
/>
);

fireEvent.click(screen.getByTestId('article-detail-card'));

expect(onClick).toHaveBeenCalledTimes(1);
});

it('calls the item onClick and not the card onClick when a recent item with onClick is clicked', () => {
const onCardClick = jest.fn();
const onItemClick = jest.fn();
const recent: PillarRecentItem[] = [
{ meta: [], onClick: onItemClick, title: 'Item 1' },
];
render(
<ContextKnowledgePillarCard
{...baseProps}
dataTestId="article-detail-card"
recent={recent}
onClick={onCardClick}
/>
);

fireEvent.click(screen.getByRole('button', { name: 'Item 1' }));

expect(onItemClick).toHaveBeenCalledTimes(1);
expect(onCardClick).not.toHaveBeenCalled();
});

it('calls onClick when the CTA button is clicked, without double-firing from the card', () => {
const onClick = jest.fn();
render(
<ContextKnowledgePillarCard
{...baseProps}
dataTestId="article-detail-card"
recent={[{ meta: [], onClick: jest.fn(), title: 'Item 1' }]}
onClick={onClick}
/>
);

fireEvent.click(screen.getByRole('button', { name: 'View all' }));

expect(onClick).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@

<div className="tw:relative tw:flex-1 tw:min-h-0 tw:overflow-y-auto">
{isLoading ? (
<Box direction="col" gap={2}>
<Box className="tw:px-4" direction="col" gap={2}>
<Skeleton height="14px" variant="rounded" width="80%" />
<Skeleton height="14px" variant="rounded" width="60%" />
<Skeleton height="14px" variant="rounded" width="70%" />
</Box>
) : isEmpty ? (

Check warning on line 65 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/ContextSimplePillarCard/ContextSimplePillarCard.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Extract this nested ternary operation into an independent statement
<EmptyPlaceholder
actions={
emptyAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@
);
};

const EmptyTags: FC = () => {
const { t } = useTranslation();

return (
<Typography className="tw:text-utility-gray-400" size="text-xs">
{t('label.no-tags-added')}
</Typography>
);
};

const LinkedAssetsReadOnly: FC<{ assets: DataAssetOption[] }> = ({
assets,
}) => {
Expand Down Expand Up @@ -238,13 +248,13 @@
viewOnly = false,
canDelete = false,
currentUserName,
}) => {

Check warning on line 251 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 36 which is greater than 10 authorized.","cost":26,"secondaryLocations":[{"line":251,"column":3,"endLine":251,"endColumn":5,"message":"+1"},{"line":255,"column":43,"endLine":255,"endColumn":45,"message":"+1"},{"line":258,"column":2,"endLine":258,"endColumn":4,"message":"+1"},{"line":260,"column":9,"endLine":260,"endColumn":11,"message":"+1"},{"line":262,"column":26,"endLine":262,"endColumn":28,"message":"+1"},{"line":266,"column":50,"endLine":266,"endColumn":52,"message":"+1"},{"line":281,"column":4,"endLine":281,"endColumn":5,"message":"+1"},{"line":644,"column":49,"endLine":644,"endColumn":51,"message":"+1"},{"line":643,"column":56,"endLine":643,"endColumn":58,"message":"+1"},{"line":655,"column":70,"endLine":655,"endColumn":72,"message":"+1"},{"line":667,"column":56,"endLine":667,"endColumn":58,"message":"+1"},{"line":667,"column":36,"endLine":667,"endColumn":38,"message":"+1"},{"line":693,"column":74,"endLine":693,"endColumn":76,"message":"+1"},{"line":693,"column":58,"endLine":693,"endColumn":60,"message":"+1"},{"line":693,"column":44,"endLine":693,"endColumn":46,"message":"+1"},{"line":693,"column":32,"endLine":693,"endColumn":34,"message":"+1"},{"line":713,"column":63,"endLine":713,"endColumn":65,"message":"+1"},{"line":722,"column":32,"endLine":722,"endColumn":34,"message":"+1"},{"line":807,"column":37,"endLine":807,"endColumn":39,"message":"+1"},{"line":831,"column":34,"endLine":831,"endColumn":35,"message":"+1"},{"line":835,"column":53,"endLine":835,"endColumn":54,"message":"+1"},{"line":979,"column":71,"endLine":979,"endColumn":73,"message":"+1"},{"line":979,"column":42,"endLine":979,"endColumn":44,"message":"+1"},{"line":1037,"column":43,"endLine":1037,"endColumn":45,"message":"+1"},{"line":1051,"column":54,"endLine":1051,"endColumn":56,"message":"+1"},{"line":1051,"column":39,"endLine":1051,"endColumn":41,"message":"+1"},{"line":1067,"column":58,"endLine":1067,"endColumn":60,"message":"+1"},{"line":1083,"column":38,"endLine":1083,"endColumn":40,"message":"+1"},{"line":1107,"column":58,"endLine":1107,"endColumn":60,"message":"+1"},{"line":1107,"column":45,"endLine":1107,"endColumn":47,"message":"+1"},{"line":1111,"column":49,"endLine":1111,"endColumn":51,"message":"+1"},{"line":1122,"column":49,"endLine":1122,"endColumn":51,"message":"+1"},{"line":1127,"column":38,"endLine":1127,"endColumn":40,"message":"+1"},{"line":1136,"column":40,"endLine":1136,"endColumn":42,"message":"+1"},{"line":1140,"column":61,"endLine":1140,"endColumn":63,"message":"+1"},{"line":1140,"column":45,"endLine":1140,"endColumn":47,"message":"+1"}]}
const { t } = useTranslation();
const modalContainerRef = useRef<HTMLDivElement>(null);
const [isViewOnly, setIsViewOnly] = useState(viewOnly);
const isEditMode = Boolean(memoryToEdit) && !isViewOnly;

let modalTitle = t('label.add-entity', { entity: t('label.memory') });

Check warning on line 257 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Define a constant instead of duplicating this literal 9 times
if (isEditMode) {
modalTitle = t('label.edit-entity', { entity: t('label.memory') });
} else if (isViewOnly) {
Expand Down Expand Up @@ -312,7 +322,7 @@
}, [viewOnly]);

// Populate / reset form whenever the memory being edited changes
useEffect(() => {

Check warning on line 325 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 11 which is greater than 10 authorized.","cost":1,"secondaryLocations":[{"line":325,"column":15,"endLine":325,"endColumn":17,"message":"+1"},{"line":326,"column":4,"endLine":326,"endColumn":6,"message":"+1"},{"line":328,"column":8,"endLine":328,"endColumn":9,"message":"+1"},{"line":332,"column":34,"endLine":332,"endColumn":36,"message":"+1"},{"line":333,"column":61,"endLine":333,"endColumn":63,"message":"+1"},{"line":333,"column":36,"endLine":333,"endColumn":38,"message":"+1"},{"line":335,"column":10,"endLine":335,"endColumn":11,"message":"+1"},{"line":338,"column":47,"endLine":338,"endColumn":49,"message":"+1"},{"line":341,"column":40,"endLine":341,"endColumn":42,"message":"+1"},{"line":351,"column":10,"endLine":351,"endColumn":11,"message":"+1"},{"line":353,"column":41,"endLine":353,"endColumn":43,"message":"+1"}]}
if (memoryToEdit) {
const memoryTypeOption = memoryToEdit.memoryType
? MEMORY_TYPE_OPTIONS.find((opt) => opt.id === memoryToEdit.memoryType)
Expand Down Expand Up @@ -351,7 +361,7 @@
setShowTagForm(false);
setModalError('');
setIsEditingVisibility(false);
}, [memoryToEdit]);

Check warning on line 364 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

React Hook useEffect has missing dependencies: 'form' and 't'. Either include them or remove the dependency array

const handleClose = () => {
form.reset(DEFAULT_FORM_VALUES);
Expand Down Expand Up @@ -405,7 +415,7 @@
[]
);

const handleSubmit = async (values: MemoryFormValues) => {

Check warning on line 418 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

{"message":"Function has a complexity of 18 which is greater than 10 authorized.","cost":8,"secondaryLocations":[{"line":418,"column":56,"endLine":418,"endColumn":58,"message":"+1"},{"line":422,"column":61,"endLine":422,"endColumn":63,"message":"+1"},{"line":434,"column":43,"endLine":434,"endColumn":44,"message":"+1"},{"line":437,"column":6,"endLine":437,"endColumn":8,"message":"+1"},{"line":437,"column":21,"endLine":437,"endColumn":23,"message":"+1"},{"line":441,"column":36,"endLine":441,"endColumn":38,"message":"+1"},{"line":442,"column":40,"endLine":442,"endColumn":42,"message":"+1"},{"line":446,"column":34,"endLine":446,"endColumn":36,"message":"+1"},{"line":448,"column":57,"endLine":448,"endColumn":59,"message":"+1"},{"line":456,"column":12,"endLine":456,"endColumn":13,"message":"+1"},{"line":473,"column":12,"endLine":473,"endColumn":13,"message":"+1"},{"line":472,"column":37,"endLine":472,"endColumn":39,"message":"+1"},{"line":483,"column":35,"endLine":483,"endColumn":37,"message":"+1"},{"line":492,"column":27,"endLine":492,"endColumn":28,"message":"+1"},{"line":493,"column":30,"endLine":493,"endColumn":31,"message":"+1"},{"line":494,"column":38,"endLine":494,"endColumn":39,"message":"+1"},{"line":495,"column":28,"endLine":495,"endColumn":29,"message":"+1"},{"line":496,"column":41,"endLine":496,"endColumn":42,"message":"+1"}]}

Check warning on line 418 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed
setModalError('');
try {
const { title, memory, memoryType, visibility } = values;
Expand All @@ -415,7 +425,7 @@
(a) => a.reference?.id && a.reference?.type
);
const toRef = (a: DataAssetOption): EntityReference => ({
id: a.reference!.id,

Check warning on line 428 in openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/CreateMemoryModal/CreateMemoryModal.component.tsx

View workflow job for this annotation

GitHub Actions / checkstyle

Forbidden non-null assertion
type: a.reference!.type,
name: a.reference?.name,
displayName: a.reference?.displayName,
Expand Down Expand Up @@ -966,6 +976,9 @@
</Typography>
</div>
<div className="tw:flex tw:items-center tw:gap-1.5 tw:flex-wrap tw:flex-1">
{isViewOnly && selectedTags.length === 0 && (
<EmptyTags />
)}
{selectedTags.map((tag) =>
isViewOnly ? (
<Badge
Expand Down
Loading
Loading