Skip to content

Commit

Permalink
[Cases] Attaching files to cases (#154436)
Browse files Browse the repository at this point in the history
Fixes #151595 

## Summary

In this PR we will be merging a feature branch into `main`.

This feature branch is a collection of several different PRs with file
functionality for cases.

- #152941
- #153957
- #154432
- #153853

Most of the code was already reviewed so this will mainly be used for
testing.

- Files tab in the case detail view.
- Attach files to a case.
- View a list of all files attached to a case (with pagination).
- Preview image files attached to a case.
- Search for files attached to a case by file name.
- Download files attached to a case.
- Users are now able to see file activity in the case detail view.
- Image files have a different icon and a clickable file name to
preview.
- Other files have a standard "document" icon and the name is not
clickable.
- The file can be downloaded by clicking the download icon.

## Release notes

Support file attachments in Cases.

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
adcoelho and kibanamachine authored Apr 18, 2023
1 parent b81a270 commit 0a38f85
Show file tree
Hide file tree
Showing 80 changed files with 3,440 additions and 115 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pageLoadAssetSize:
banners: 17946
bfetch: 22837
canvas: 1066647
cases: 144442
cases: 170000
charts: 55000
cloud: 21076
cloudChat: 19894
Expand Down
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
16 changes: 8 additions & 8 deletions x-pack/plugins/cases/common/api/cases/comment/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import * as rt from 'io-ts';
import { MAX_DELETE_FILES } from '../../../constants';
import { limitedArraySchema, NonEmptyString } from '../../../schema';

export const SingleFileAttachmentMetadataRt = rt.type({
name: rt.string,
extension: rt.string,
mimeType: rt.string,
created: rt.string,
});

export const FileAttachmentMetadataRt = rt.type({
files: rt.array(
rt.type({
name: rt.string,
extension: rt.string,
mimeType: rt.string,
createdAt: rt.string,
})
),
files: rt.array(SingleFileAttachmentMetadataRt),
});

export type FileAttachmentMetadata = rt.TypeOf<typeof FileAttachmentMetadataRt>;
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
28 changes: 24 additions & 4 deletions x-pack/plugins/cases/public/client/attachment_framework/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,38 @@ import type {
} from '../../../common/api';
import type { Case } from '../../containers/types';

export interface AttachmentAction {
export enum AttachmentActionType {
BUTTON = 'button',
CUSTOM = 'custom',
}

interface BaseAttachmentAction {
type: AttachmentActionType;
label: string;
isPrimary?: boolean;
disabled?: boolean;
}

interface ButtonAttachmentAction extends BaseAttachmentAction {
type: AttachmentActionType.BUTTON;
onClick: () => void;
iconType: string;
label: string;
color?: EuiButtonProps['color'];
isPrimary?: boolean;
}

interface CustomAttachmentAction extends BaseAttachmentAction {
type: AttachmentActionType.CUSTOM;
render: () => JSX.Element;
}

export type AttachmentAction = ButtonAttachmentAction | CustomAttachmentAction;

export interface AttachmentViewObject<Props = {}> {
timelineAvatar?: EuiCommentProps['timelineAvatar'];
getActions?: (props: Props) => AttachmentAction[];
event?: EuiCommentProps['event'];
children?: React.LazyExoticComponent<React.FC<Props>>;
hideDefaultActions?: boolean;
}

export interface CommonAttachmentViewProps {
Expand All @@ -46,8 +65,9 @@ export interface AttachmentType<Props> {
id: string;
icon: IconType;
displayName: string;
getAttachmentViewObject: () => AttachmentViewObject<Props>;
getAttachmentViewObject: (props: Props) => AttachmentViewObject<Props>;
getAttachmentRemovalObject?: (props: Props) => Pick<AttachmentViewObject<Props>, 'event'>;
hideDefaultActions?: boolean;
}

export type ExternalReferenceAttachmentType = AttachmentType<ExternalReferenceAttachmentViewProps>;
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
Loading

0 comments on commit 0a38f85

Please sign in to comment.