Skip to content

Commit

Permalink
[Cases] Detail View Attachments Table (#152941)
Browse files Browse the repository at this point in the history
Fixes #151723
This PR adds a way for users to:
View a list of all files attached to a case(with pagination).
Attach files to a case.
Preview image files attached to a case.
Search for files attached to a case by file name.
Download files attached to a case.
  • Loading branch information
adcoelho authored Mar 27, 2023
1 parent 1a0cab8 commit 78133a7
Show file tree
Hide file tree
Showing 43 changed files with 1,689 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { context } from './context';
/**
* An object representing an uploaded file
*/
interface UploadedFile<Meta = unknown> {
export interface UploadedFile<Meta = unknown> {
/**
* The ID that was generated for the uploaded file
*/
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/cases/common/constants/mime_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/**
* These were retrieved from https://www.iana.org/assignments/media-types/media-types.xhtml#image
*/
const imageMimeTypes = [
export const imageMimeTypes = [
'image/aces',
'image/apng',
'image/avci',
Expand Down Expand Up @@ -87,9 +87,9 @@ const imageMimeTypes = [
'image/wmf',
];

const textMimeTypes = ['text/plain', 'text/csv', 'text/json', 'application/json'];
export const textMimeTypes = ['text/plain', 'text/csv', 'text/json', 'application/json'];

const compressionMimeTypes = [
export const compressionMimeTypes = [
'application/zip',
'application/gzip',
'application/x-bzip',
Expand All @@ -98,7 +98,7 @@ const compressionMimeTypes = [
'application/x-tar',
];

const pdfMimeTypes = ['application/pdf'];
export const pdfMimeTypes = ['application/pdf'];

export const ALLOWED_MIME_TYPES = [
...imageMimeTypes,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/cases/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export type SnakeToCamelCase<T> = T extends Record<string, unknown>
export enum CASE_VIEW_PAGE_TABS {
ALERTS = 'alerts',
ACTIVITY = 'activity',
FILES = 'files',
}
21 changes: 15 additions & 6 deletions x-pack/plugins/cases/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router-dom';

import { I18nProvider } from '@kbn/i18n-react';
import { EuiErrorBoundary } from '@elastic/eui';

import { I18nProvider } from '@kbn/i18n-react';
import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common';
import {
KibanaContextProvider,
KibanaThemeProvider,
useUiSetting$,
} from '@kbn/kibana-react-plugin/public';
import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common';
import type { RenderAppProps } from './types';
import { CasesApp } from './components/app';

import type { ScopedFilesClient } from '@kbn/files-plugin/public';
import type { ExternalReferenceAttachmentTypeRegistry } from './client/attachment_framework/external_reference_registry';
import type { PersistableStateAttachmentTypeRegistry } from './client/attachment_framework/persistable_state_registry';
import type { RenderAppProps } from './types';

import { CasesApp } from './components/app';

export const renderApp = (deps: RenderAppProps) => {
const { mountParams } = deps;
Expand All @@ -37,17 +39,23 @@ export const renderApp = (deps: RenderAppProps) => {
interface CasesAppWithContextProps {
externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry;
persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry;
getFilesClient: (scope: string) => ScopedFilesClient;
}

const CasesAppWithContext: React.FC<CasesAppWithContextProps> = React.memo(
({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry }) => {
({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
}) => {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');

return (
<StyledComponentsThemeProvider darkMode={darkMode}>
<CasesApp
externalReferenceAttachmentTypeRegistry={externalReferenceAttachmentTypeRegistry}
persistableStateAttachmentTypeRegistry={persistableStateAttachmentTypeRegistry}
getFilesClient={getFilesClient}
/>
</StyledComponentsThemeProvider>
);
Expand Down Expand Up @@ -78,6 +86,7 @@ export const App: React.FC<{ deps: RenderAppProps }> = ({ deps }) => {
deps.externalReferenceAttachmentTypeRegistry
}
persistableStateAttachmentTypeRegistry={deps.persistableStateAttachmentTypeRegistry}
getFilesClient={pluginsStart.files.filesClientFactory.asScoped}
/>
</Router>
</KibanaContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context';
type GetAllCasesSelectorModalPropsInternal = AllCasesSelectorModalProps & CasesContextProps;
export type GetAllCasesSelectorModalProps = Omit<
GetAllCasesSelectorModalPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const AllCasesSelectorModalLazy: React.FC<AllCasesSelectorModalProps> = lazy(
Expand All @@ -23,6 +25,7 @@ const AllCasesSelectorModalLazy: React.FC<AllCasesSelectorModalProps> = lazy(
export const getAllCasesSelectorModalLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
hiddenStatuses,
Expand All @@ -33,6 +36,7 @@ export const getAllCasesSelectorModalLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
}}
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/cases/public/client/ui/get_cases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import { CasesProvider } from '../../components/cases_context';
type GetCasesPropsInternal = CasesProps & CasesContextProps;
export type GetCasesProps = Omit<
GetCasesPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const CasesRoutesLazy: React.FC<CasesProps> = lazy(() => import('../../components/app/routes'));

export const getCasesLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
basePath,
Expand All @@ -39,6 +42,7 @@ export const getCasesLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
basePath,
Expand Down
12 changes: 10 additions & 2 deletions x-pack/plugins/cases/public/client/ui/get_cases_context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import type { CasesContextProps } from '../../components/cases_context';
export type GetCasesContextPropsInternal = CasesContextProps;
export type GetCasesContextProps = Omit<
CasesContextProps,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const CasesProviderLazy: React.FC<{ value: GetCasesContextPropsInternal }> = lazy(
Expand All @@ -28,6 +30,7 @@ const CasesProviderLazyWrapper = ({
features,
children,
releasePhase,
getFilesClient,
}: GetCasesContextPropsInternal & { children: ReactNode }) => {
return (
<Suspense fallback={<EuiLoadingSpinner />}>
Expand All @@ -39,6 +42,7 @@ const CasesProviderLazyWrapper = ({
permissions,
features,
releasePhase,
getFilesClient,
}}
>
{children}
Expand All @@ -52,9 +56,12 @@ CasesProviderLazyWrapper.displayName = 'CasesProviderLazyWrapper';
export const getCasesContextLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
}: Pick<
GetCasesContextPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>): (() => React.FC<GetCasesContextProps>) => {
const CasesProviderLazyWrapperWithRegistry: React.FC<GetCasesContextProps> = ({
children,
Expand All @@ -64,6 +71,7 @@ export const getCasesContextLazy = ({
{...props}
externalReferenceAttachmentTypeRegistry={externalReferenceAttachmentTypeRegistry}
persistableStateAttachmentTypeRegistry={persistableStateAttachmentTypeRegistry}
getFilesClient={getFilesClient}
>
{children}
</CasesProviderLazyWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context';
type GetCreateCaseFlyoutPropsInternal = CreateCaseFlyoutProps & CasesContextProps;
export type GetCreateCaseFlyoutProps = Omit<
GetCreateCaseFlyoutPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

export const CreateCaseFlyoutLazy: React.FC<CreateCaseFlyoutProps> = lazy(
Expand All @@ -23,6 +25,7 @@ export const CreateCaseFlyoutLazy: React.FC<CreateCaseFlyoutProps> = lazy(
export const getCreateCaseFlyoutLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
features,
Expand All @@ -35,6 +38,7 @@ export const getCreateCaseFlyoutLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
features,
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import type { RecentCasesProps } from '../../components/recent_cases';
type GetRecentCasesPropsInternal = RecentCasesProps & CasesContextProps;
export type GetRecentCasesProps = Omit<
GetRecentCasesPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const RecentCasesLazy: React.FC<RecentCasesProps> = lazy(
Expand All @@ -23,6 +25,7 @@ const RecentCasesLazy: React.FC<RecentCasesProps> = lazy(
export const getRecentCasesLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
maxCasesToShow,
Expand All @@ -31,6 +34,7 @@ export const getRecentCasesLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
}}
Expand Down
45 changes: 35 additions & 10 deletions x-pack/plugins/cases/public/common/mock/test_providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,29 @@

import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';

import type { RenderOptions, RenderResult } from '@testing-library/react';
import type { ILicense } from '@kbn/licensing-plugin/public';
import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import type { ScopedFilesClient } from '@kbn/files-plugin/public';

import { euiDarkVars } from '@kbn/ui-theme';
import { I18nProvider } from '@kbn/i18n-react';
import { ThemeProvider } from 'styled-components';
import { createMockFilesClient } from '@kbn/shared-ux-file-mocks';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import type { RenderOptions, RenderResult } from '@testing-library/react';
import { render as reactRender } from '@testing-library/react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { ILicense } from '@kbn/licensing-plugin/public';
import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { FilesContext } from '@kbn/shared-ux-file-context';

import type { CasesFeatures, CasesPermissions } from '../../../common/ui/types';
import { CasesProvider } from '../../components/cases_context';
import { createStartServicesMock } from '../lib/kibana/kibana_react.mock';
import type { StartServices } from '../../types';
import type { ReleasePhase } from '../../components/types';

import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { CasesProvider } from '../../components/cases_context';
import { createStartServicesMock } from '../lib/kibana/kibana_react.mock';
import { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry';
import { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry';
import { allCasesPermissions } from './permissions';
Expand All @@ -37,21 +44,36 @@ interface TestProviderProps {
releasePhase?: ReleasePhase;
externalReferenceAttachmentTypeRegistry?: ExternalReferenceAttachmentTypeRegistry;
persistableStateAttachmentTypeRegistry?: PersistableStateAttachmentTypeRegistry;
getFilesClient?: (scope: string) => ScopedFilesClient;
license?: ILicense;
}
type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;

window.scrollTo = jest.fn();

export const mockedFilesClient = createMockFilesClient() as unknown as ScopedFilesClient<unknown>;

// @ts-ignore
mockedFilesClient.getFileKind.mockImplementation(() => ({
id: 'test',
maxSizeBytes: 10000,
http: {},
}));

const mockGetFilesClient = () => mockedFilesClient;

export const mockedTestProvidersOwner = [SECURITY_SOLUTION_OWNER];

/** A utility for wrapping children in the providers required to run most tests */
const TestProvidersComponent: React.FC<TestProviderProps> = ({
children,
features,
owner = [SECURITY_SOLUTION_OWNER],
owner = mockedTestProvidersOwner,
permissions = allCasesPermissions(),
releasePhase = 'ga',
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(),
getFilesClient = mockGetFilesClient,
license,
}) => {
const queryClient = new QueryClient({
Expand Down Expand Up @@ -82,9 +104,10 @@ const TestProvidersComponent: React.FC<TestProviderProps> = ({
features,
owner,
permissions,
getFilesClient,
}}
>
{children}
<FilesContext client={createMockFilesClient()}>{children}</FilesContext>
</CasesProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down Expand Up @@ -125,11 +148,12 @@ export const testQueryClient = new QueryClient({

export const createAppMockRenderer = ({
features,
owner = [SECURITY_SOLUTION_OWNER],
owner = mockedTestProvidersOwner,
permissions = allCasesPermissions(),
releasePhase = 'ga',
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(),
getFilesClient = mockGetFilesClient,
license,
}: Omit<TestProviderProps, 'children'> = {}): AppMockRenderer => {
const services = createStartServicesMock({ license });
Expand Down Expand Up @@ -161,6 +185,7 @@ export const createAppMockRenderer = ({
owner,
permissions,
releasePhase,
getFilesClient,
}}
>
{children}
Expand Down
Loading

0 comments on commit 78133a7

Please sign in to comment.