feat(rca): items management api (#190852)

This commit is contained in:
Kevin Delemme 2024-08-21 16:34:33 -04:00 committed by GitHub
parent 24b4eb4d18
commit ca6f3edf0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 434 additions and 126 deletions

View file

@ -6,22 +6,4 @@
* Side Public License, v 1.
*/
export type * from './src/schema/create';
export type * from './src/schema/create_notes';
export type * from './src/schema/delete';
export type * from './src/schema/find';
export type * from './src/schema/get';
export type * from './src/schema/get_notes';
export type * from './src/schema/origin';
export type * from './src/schema/delete_note';
export type * from './src/schema/investigation_note';
export * from './src/schema/create';
export * from './src/schema/create_notes';
export * from './src/schema/delete';
export * from './src/schema/find';
export * from './src/schema/get';
export * from './src/schema/get_notes';
export * from './src/schema/origin';
export * from './src/schema/delete_note';
export * from './src/schema/investigation_note';
export * from './src';

View file

@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './rest_specs';
export * from './schema';

View file

@ -8,7 +8,7 @@
import * as t from 'io-ts';
import { investigationResponseSchema } from './investigation';
import { alertOriginSchema, blankOriginSchema } from './origin';
import { alertOriginSchema, blankOriginSchema } from '../schema';
const createInvestigationParamsSchema = t.type({
body: t.type({
@ -23,9 +23,8 @@ const createInvestigationParamsSchema = t.type({
const createInvestigationResponseSchema = investigationResponseSchema;
type CreateInvestigationInput = t.OutputOf<typeof createInvestigationParamsSchema.props.body>; // Raw payload sent by the frontend
type CreateInvestigationParams = t.TypeOf<typeof createInvestigationParamsSchema.props.body>; // Parsed payload used by the backend
type CreateInvestigationResponse = t.OutputOf<typeof createInvestigationResponseSchema>; // Raw response sent to the frontend
type CreateInvestigationParams = t.TypeOf<typeof createInvestigationParamsSchema.props.body>;
type CreateInvestigationResponse = t.OutputOf<typeof createInvestigationResponseSchema>;
export { createInvestigationParamsSchema, createInvestigationResponseSchema };
export type { CreateInvestigationInput, CreateInvestigationParams, CreateInvestigationResponse };
export type { CreateInvestigationParams, CreateInvestigationResponse };

View file

@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
import { investigationItemsSchema } from '../schema';
import { investigationItemResponseSchema } from './investigation_item';
const createInvestigationItemParamsSchema = t.type({
path: t.type({
investigationId: t.string,
}),
body: investigationItemsSchema,
});
const createInvestigationItemResponseSchema = investigationItemResponseSchema;
type CreateInvestigationItemParams = t.TypeOf<
typeof createInvestigationItemParamsSchema.props.body
>;
type CreateInvestigationItemResponse = t.OutputOf<typeof createInvestigationItemResponseSchema>;
export { createInvestigationItemParamsSchema, createInvestigationItemResponseSchema };
export type { CreateInvestigationItemParams, CreateInvestigationItemResponse };

View file

@ -11,7 +11,7 @@ import { investigationNoteResponseSchema } from './investigation_note';
const createInvestigationNoteParamsSchema = t.type({
path: t.type({
id: t.string,
investigationId: t.string,
}),
body: t.type({
content: t.string,
@ -20,17 +20,10 @@ const createInvestigationNoteParamsSchema = t.type({
const createInvestigationNoteResponseSchema = investigationNoteResponseSchema;
type CreateInvestigationNoteInput = t.OutputOf<
typeof createInvestigationNoteParamsSchema.props.body
>;
type CreateInvestigationNoteParams = t.TypeOf<
typeof createInvestigationNoteParamsSchema.props.body
>;
type CreateInvestigationNoteResponse = t.OutputOf<typeof createInvestigationNoteResponseSchema>;
export { createInvestigationNoteParamsSchema, createInvestigationNoteResponseSchema };
export type {
CreateInvestigationNoteInput,
CreateInvestigationNoteParams,
CreateInvestigationNoteResponse,
};
export type { CreateInvestigationNoteParams, CreateInvestigationNoteResponse };

View file

@ -10,11 +10,11 @@ import * as t from 'io-ts';
const deleteInvestigationParamsSchema = t.type({
path: t.type({
id: t.string,
investigationId: t.string,
}),
});
type DeleteInvestigationParams = t.TypeOf<typeof deleteInvestigationParamsSchema.props.path>; // Parsed payload used by the backend
type DeleteInvestigationParams = t.TypeOf<typeof deleteInvestigationParamsSchema.props.path>;
export { deleteInvestigationParamsSchema };
export type { DeleteInvestigationParams };

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
const deleteInvestigationItemParamsSchema = t.type({
path: t.type({
investigationId: t.string,
itemId: t.string,
}),
});
type DeleteInvestigationItemParams = t.TypeOf<
typeof deleteInvestigationItemParamsSchema.props.path
>;
export { deleteInvestigationItemParamsSchema };
export type { DeleteInvestigationItemParams };

View file

@ -10,14 +10,14 @@ import * as t from 'io-ts';
const deleteInvestigationNoteParamsSchema = t.type({
path: t.type({
id: t.string,
investigationId: t.string,
noteId: t.string,
}),
});
type DeleteInvestigationNoteParams = t.TypeOf<
typeof deleteInvestigationNoteParamsSchema.props.path
>; // Parsed payload used by the backend
>;
export { deleteInvestigationNoteParamsSchema };
export type { DeleteInvestigationNoteParams };

View file

@ -24,8 +24,8 @@ const findInvestigationsResponseSchema = t.type({
results: t.array(investigationResponseSchema),
});
type FindInvestigationsParams = t.TypeOf<typeof findInvestigationsParamsSchema.props.query>; // Parsed payload used by the backend
type FindInvestigationsResponse = t.OutputOf<typeof findInvestigationsResponseSchema>; // Raw response sent to the frontend
type FindInvestigationsParams = t.TypeOf<typeof findInvestigationsParamsSchema.props.query>;
type FindInvestigationsResponse = t.OutputOf<typeof findInvestigationsResponseSchema>;
export { findInvestigationsParamsSchema, findInvestigationsResponseSchema };
export type { FindInvestigationsParams, FindInvestigationsResponse };

View file

@ -11,7 +11,7 @@ import { investigationResponseSchema } from './investigation';
const getInvestigationParamsSchema = t.type({
path: t.type({
id: t.string,
investigationId: t.string,
}),
});

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
import { investigationItemResponseSchema } from './investigation_item';
const getInvestigationItemsParamsSchema = t.type({
path: t.type({
investigationId: t.string,
}),
});
const getInvestigationItemsResponseSchema = t.array(investigationItemResponseSchema);
type GetInvestigationItemsResponse = t.OutputOf<typeof getInvestigationItemsResponseSchema>;
export { getInvestigationItemsParamsSchema, getInvestigationItemsResponseSchema };
export type { GetInvestigationItemsResponse };

View file

@ -11,7 +11,7 @@ import { investigationNoteResponseSchema } from './investigation_note';
const getInvestigationNotesParamsSchema = t.type({
path: t.type({
id: t.string,
investigationId: t.string,
}),
});

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export type * from './create';
export type * from './create_note';
export type * from './delete';
export type * from './find';
export type * from './get';
export type * from './get_notes';
export type * from './delete_note';
export type * from './investigation_note';
export type * from './create_item';
export type * from './delete_item';
export type * from './get_items';
export * from './create';
export * from './create_note';
export * from './delete';
export * from './find';
export * from './get';
export * from './get_notes';
export * from './delete_note';
export * from './investigation_note';
export * from './create_item';
export * from './delete_item';
export * from './get_items';

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
import { investigationSchema } from '../schema';
const investigationResponseSchema = investigationSchema;
type InvestigationResponse = t.OutputOf<typeof investigationResponseSchema>;
export { investigationResponseSchema };
export type { InvestigationResponse };

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
import { investigationItemSchema } from '../schema';
const investigationItemResponseSchema = investigationItemSchema;
type InvestigationItemResponse = t.OutputOf<typeof investigationItemResponseSchema>;
export { investigationItemResponseSchema };
export type { InvestigationItemResponse };

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
import { investigationNoteSchema } from '../schema';
const investigationNoteResponseSchema = investigationNoteSchema;
type InvestigationNoteResponse = t.OutputOf<typeof investigationNoteResponseSchema>;
export { investigationNoteResponseSchema };
export type { InvestigationNoteResponse };

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './investigation';
export * from './investigation_item';
export * from './investigation_note';
export * from './origin';

View file

@ -8,9 +8,10 @@
import * as t from 'io-ts';
import { alertOriginSchema, blankOriginSchema } from './origin';
import { investigationNoteResponseSchema } from './investigation_note';
import { investigationNoteSchema } from './investigation_note';
import { investigationItemSchema } from './investigation_item';
const investigationResponseSchema = t.type({
const investigationSchema = t.type({
id: t.string,
title: t.string,
createdAt: t.number,
@ -20,10 +21,8 @@ const investigationResponseSchema = t.type({
}),
origin: t.union([alertOriginSchema, blankOriginSchema]),
status: t.union([t.literal('ongoing'), t.literal('closed')]),
notes: t.array(investigationNoteResponseSchema),
notes: t.array(investigationNoteSchema),
items: t.array(investigationItemSchema),
});
type InvestigationResponse = t.OutputOf<typeof investigationResponseSchema>;
export { investigationResponseSchema };
export type { InvestigationResponse };
export { investigationSchema };

View file

@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as t from 'io-ts';
const esqlItemSchema = t.type({
title: t.string,
type: t.literal('esql'),
params: t.type({
esql: t.string,
suggestion: t.any,
}),
});
const investigationItemsSchema = esqlItemSchema; // replace with union with various item types
const investigationItemSchema = t.intersection([
t.type({ id: t.string, createdAt: t.number, createdBy: t.string }),
investigationItemsSchema,
]);
export { investigationItemSchema, investigationItemsSchema, esqlItemSchema };

View file

@ -8,14 +8,11 @@
import * as t from 'io-ts';
const investigationNoteResponseSchema = t.type({
const investigationNoteSchema = t.type({
id: t.string,
content: t.string,
createdAt: t.number,
createdBy: t.string,
});
type InvestigationNoteResponse = t.OutputOf<typeof investigationNoteResponseSchema>;
export { investigationNoteResponseSchema };
export type { InvestigationNoteResponse };
export { investigationNoteSchema };

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import type { AuthenticatedUser } from '@kbn/core/public';
import type { DeepPartial } from 'utility-types';
export interface GlobalWidgetParameters {
@ -36,13 +35,12 @@ export interface InvestigateWidget<
TData extends Record<string, any> = {}
> {
id: string;
created: number;
last_updated: number;
createdAt: number;
createdBy: string;
title: string;
type: string;
user: AuthenticatedUser;
parameters: GlobalWidgetParameters & TParameters;
data: TData;
title: string;
}
export type InvestigateWidgetCreate<TParameters extends Record<string, any> = {}> = Pick<

View file

@ -42,12 +42,11 @@ export async function regenerateItem({
});
return {
created: now,
createdAt: now,
id: v4(),
...widget,
parameters: nextParameters,
data: widgetData,
user,
last_updated: now,
createdBy: user.username,
};
}

View file

@ -25,9 +25,6 @@ function WithPersistedChanges(props: React.ComponentProps<typeof Component>) {
return (
<Component
{...props}
onItemsChange={async (nextItems) => {
setItems(() => nextItems);
}}
onItemCopy={async (item) => {
setItems((prevItems) =>
prevItems.concat({

View file

@ -20,14 +20,12 @@ export interface InvestigateWidgetGridItem {
interface InvestigateWidgetGridProps {
items: InvestigateWidgetGridItem[];
onItemsChange: (items: InvestigateWidgetGridItem[]) => Promise<void>;
onItemCopy: (item: InvestigateWidgetGridItem) => Promise<void>;
onItemDelete: (item: InvestigateWidgetGridItem) => Promise<void>;
}
export function InvestigateWidgetGrid({
items,
onItemsChange,
onItemDelete,
onItemCopy,
}: InvestigateWidgetGridProps) {

View file

@ -7,7 +7,7 @@
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
import {
CreateInvestigationNoteInput,
CreateInvestigationNoteParams,
CreateInvestigationNoteResponse,
} from '@kbn/investigation-shared';
import { useMutation } from '@tanstack/react-query';
@ -26,7 +26,7 @@ export function useAddInvestigationNote() {
return useMutation<
CreateInvestigationNoteResponse,
ServerError,
{ investigationId: string; note: CreateInvestigationNoteInput },
{ investigationId: string; note: CreateInvestigationNoteParams },
{ investigationId: string }
>(
['addInvestigationNote'],

View file

@ -7,7 +7,6 @@
import datemath from '@elastic/datemath';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import { AuthenticatedUser } from '@kbn/security-plugin/common';
import { noop } from 'lodash';
import React from 'react';
import useAsync from 'react-use/lib/useAsync';
import { AddObservationUI } from '../../../../components/add_observation_ui';
@ -81,9 +80,6 @@ function InvestigationDetailsWithUser({
<EuiFlexItem grow={false}>
<InvestigateWidgetGrid
items={renderableInvestigation.items}
onItemsChange={async (nextGridItems) => {
noop();
}}
onItemCopy={async (copiedItem) => {
return copyItem(copiedItem.id);
}}

View file

@ -5,22 +5,8 @@
* 2.0.
*/
import { alertOriginSchema, blankOriginSchema } from '@kbn/investigation-shared';
import { investigationSchema } from '@kbn/investigation-shared';
import * as t from 'io-ts';
import { investigationNoteSchema } from './investigation_note';
export const investigationSchema = t.type({
id: t.string,
title: t.string,
createdAt: t.number,
createdBy: t.string,
params: t.type({
timeRange: t.type({ from: t.number, to: t.number }),
}),
origin: t.union([alertOriginSchema, blankOriginSchema]),
status: t.union([t.literal('ongoing'), t.literal('closed')]),
notes: t.array(investigationNoteSchema),
});
export type Investigation = t.TypeOf<typeof investigationSchema>;
export type StoredInvestigation = t.OutputOf<typeof investigationSchema>;

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as t from 'io-ts';
import { investigationItemSchema } from '@kbn/investigation-shared';
export type InvestigationItem = t.TypeOf<typeof investigationItemSchema>;
export type StoredInvestigationItem = t.OutputOf<typeof investigationItemSchema>;

View file

@ -6,13 +6,7 @@
*/
import * as t from 'io-ts';
export const investigationNoteSchema = t.type({
id: t.string,
createdAt: t.number,
createdBy: t.string,
content: t.string,
});
import { investigationNoteSchema } from '@kbn/investigation-shared';
export type InvestigationNote = t.TypeOf<typeof investigationNoteSchema>;
export type StoredInvestigationNote = t.OutputOf<typeof investigationNoteSchema>;

View file

@ -6,23 +6,29 @@
*/
import {
createInvestigationItemParamsSchema,
createInvestigationNoteParamsSchema,
createInvestigationParamsSchema,
deleteInvestigationItemParamsSchema,
deleteInvestigationNoteParamsSchema,
deleteInvestigationParamsSchema,
findInvestigationsParamsSchema,
getInvestigationItemsParamsSchema,
getInvestigationNotesParamsSchema,
getInvestigationParamsSchema,
} from '@kbn/investigation-shared';
import { createInvestigation } from '../services/create_investigation';
import { createInvestigationItem } from '../services/create_investigation_item';
import { createInvestigationNote } from '../services/create_investigation_note';
import { deleteInvestigation } from '../services/delete_investigation';
import { deleteInvestigationItem } from '../services/delete_investigation_item';
import { deleteInvestigationNote } from '../services/delete_investigation_note';
import { findInvestigations } from '../services/find_investigations';
import { getInvestigation } from '../services/get_investigation';
import { getInvestigationNotes } from '../services/get_investigation_notes';
import { investigationRepositoryFactory } from '../services/investigation_repository';
import { createInvestigateAppServerRoute } from './create_investigate_app_server_route';
import { deleteInvestigationNote } from '../services/delete_investigation_note';
import { getInvestigationItems } from '../services/get_investigation_items';
const createInvestigationRoute = createInvestigateAppServerRoute({
endpoint: 'POST /api/observability/investigations 2023-10-31',
@ -57,35 +63,35 @@ const findInvestigationsRoute = createInvestigateAppServerRoute({
});
const getInvestigationRoute = createInvestigateAppServerRoute({
endpoint: 'GET /api/observability/investigations/{id} 2023-10-31',
endpoint: 'GET /api/observability/investigations/{investigationId} 2023-10-31',
options: {
tags: [],
},
params: getInvestigationParamsSchema,
handler: async (params) => {
const soClient = (await params.context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger: params.logger });
handler: async ({ params, context, logger }) => {
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await getInvestigation(params.params.path, repository);
return await getInvestigation(params.path, repository);
},
});
const deleteInvestigationRoute = createInvestigateAppServerRoute({
endpoint: 'DELETE /api/observability/investigations/{id} 2023-10-31',
endpoint: 'DELETE /api/observability/investigations/{investigationId} 2023-10-31',
options: {
tags: [],
},
params: deleteInvestigationParamsSchema,
handler: async (params) => {
const soClient = (await params.context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger: params.logger });
handler: async ({ params, context, logger }) => {
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await deleteInvestigation(params.params.path.id, repository);
return await deleteInvestigation(params.path.investigationId, repository);
},
});
const createInvestigationNoteRoute = createInvestigateAppServerRoute({
endpoint: 'POST /api/observability/investigations/{id}/notes 2023-10-31',
endpoint: 'POST /api/observability/investigations/{investigationId}/notes 2023-10-31',
options: {
tags: [],
},
@ -98,26 +104,29 @@ const createInvestigationNoteRoute = createInvestigateAppServerRoute({
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await createInvestigationNote(params.path.id, params.body, { repository, user });
return await createInvestigationNote(params.path.investigationId, params.body, {
repository,
user,
});
},
});
const getInvestigationNotesRoute = createInvestigateAppServerRoute({
endpoint: 'GET /api/observability/investigations/{id}/notes 2023-10-31',
endpoint: 'GET /api/observability/investigations/{investigationId}/notes 2023-10-31',
options: {
tags: [],
},
params: getInvestigationNotesParamsSchema,
handler: async (params) => {
const soClient = (await params.context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger: params.logger });
handler: async ({ params, context, request, logger }) => {
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await getInvestigationNotes(params.params.path.id, repository);
return await getInvestigationNotes(params.path.investigationId, repository);
},
});
const deleteInvestigationNotesRoute = createInvestigateAppServerRoute({
endpoint: 'DELETE /api/observability/investigations/{id}/notes/{noteId} 2023-10-31',
endpoint: 'DELETE /api/observability/investigations/{investigationId}/notes/{noteId} 2023-10-31',
options: {
tags: [],
},
@ -130,7 +139,63 @@ const deleteInvestigationNotesRoute = createInvestigateAppServerRoute({
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await deleteInvestigationNote(params.path.id, params.path.noteId, {
return await deleteInvestigationNote(params.path.investigationId, params.path.noteId, {
repository,
user,
});
},
});
const createInvestigationItemRoute = createInvestigateAppServerRoute({
endpoint: 'POST /api/observability/investigations/{investigationId}/items 2023-10-31',
options: {
tags: [],
},
params: createInvestigationItemParamsSchema,
handler: async ({ params, context, request, logger }) => {
const user = (await context.core).coreStart.security.authc.getCurrentUser(request);
if (!user) {
throw new Error('User is not authenticated');
}
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await createInvestigationItem(params.path.investigationId, params.body, {
repository,
user,
});
},
});
const getInvestigationItemsRoute = createInvestigateAppServerRoute({
endpoint: 'GET /api/observability/investigations/{investigationId}/items 2023-10-31',
options: {
tags: [],
},
params: getInvestigationItemsParamsSchema,
handler: async ({ params, context, request, logger }) => {
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await getInvestigationItems(params.path.investigationId, repository);
},
});
const deleteInvestigationItemRoute = createInvestigateAppServerRoute({
endpoint: 'DELETE /api/observability/investigations/{investigationId}/items/{itemId} 2023-10-31',
options: {
tags: [],
},
params: deleteInvestigationItemParamsSchema,
handler: async ({ params, context, request, logger }) => {
const user = (await context.core).coreStart.security.authc.getCurrentUser(request);
if (!user) {
throw new Error('User is not authenticated');
}
const soClient = (await context.core).savedObjects.client;
const repository = investigationRepositoryFactory({ soClient, logger });
return await deleteInvestigationItem(params.path.investigationId, params.path.itemId, {
repository,
user,
});
@ -146,6 +211,9 @@ export function getGlobalInvestigateAppServerRouteRepository() {
...createInvestigationNoteRoute,
...getInvestigationNotesRoute,
...deleteInvestigationNotesRoute,
...createInvestigationItemRoute,
...deleteInvestigationItemRoute,
...getInvestigationItemsRoute,
};
}

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { CreateInvestigationInput, CreateInvestigationResponse } from '@kbn/investigation-shared';
import { CreateInvestigationParams, CreateInvestigationResponse } from '@kbn/investigation-shared';
import type { AuthenticatedUser } from '@kbn/core-security-common';
import { InvestigationRepository } from './investigation_repository';
@ -15,7 +15,7 @@ enum InvestigationStatus {
}
export async function createInvestigation(
params: CreateInvestigationInput,
params: CreateInvestigationParams,
{ repository, user }: { repository: InvestigationRepository; user: AuthenticatedUser }
): Promise<CreateInvestigationResponse> {
const investigation = {
@ -24,6 +24,7 @@ export async function createInvestigation(
createdBy: user.username,
status: InvestigationStatus.ongoing,
notes: [],
items: [],
};
await repository.save(investigation);

View file

@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { AuthenticatedUser } from '@kbn/core-security-common';
import {
CreateInvestigationItemParams,
CreateInvestigationItemResponse,
} from '@kbn/investigation-shared';
import { v4 } from 'uuid';
import { InvestigationRepository } from './investigation_repository';
export async function createInvestigationItem(
investigationId: string,
params: CreateInvestigationItemParams,
{ repository, user }: { repository: InvestigationRepository; user: AuthenticatedUser }
): Promise<CreateInvestigationItemResponse> {
const investigation = await repository.findById(investigationId);
const investigationItem = {
id: v4(),
createdBy: user.username,
createdAt: Date.now(),
...params,
};
investigation.items.push(investigationItem);
await repository.save(investigation);
return investigationItem;
}

View file

@ -5,17 +5,17 @@
* 2.0.
*/
import type { AuthenticatedUser } from '@kbn/core-security-common';
import {
CreateInvestigationNoteInput,
CreateInvestigationNoteParams,
CreateInvestigationNoteResponse,
} from '@kbn/investigation-shared';
import { v4 } from 'uuid';
import type { AuthenticatedUser } from '@kbn/core-security-common';
import { InvestigationRepository } from './investigation_repository';
export async function createInvestigationNote(
investigationId: string,
params: CreateInvestigationNoteInput,
params: CreateInvestigationNoteParams,
{ repository, user }: { repository: InvestigationRepository; user: AuthenticatedUser }
): Promise<CreateInvestigationNoteResponse> {
const investigation = await repository.findById(investigationId);

View file

@ -8,8 +8,8 @@
import { InvestigationRepository } from './investigation_repository';
export async function deleteInvestigation(
id: string,
investigationId: string,
repository: InvestigationRepository
): Promise<void> {
await repository.deleteById(id);
await repository.deleteById(investigationId);
}

View file

@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { AuthenticatedUser } from '@kbn/core-security-common';
import { InvestigationRepository } from './investigation_repository';
export async function deleteInvestigationItem(
investigationId: string,
itemId: string,
{ repository, user }: { repository: InvestigationRepository; user: AuthenticatedUser }
): Promise<void> {
const investigation = await repository.findById(investigationId);
const item = investigation.items.find((currItem) => currItem.id === itemId);
if (!item) {
throw new Error('Note not found');
}
if (item.createdBy !== user.username) {
throw new Error('User does not have permission to delete note');
}
investigation.items = investigation.items.filter((currItem) => currItem.id !== itemId);
await repository.save(investigation);
}

View file

@ -16,7 +16,7 @@ export async function getInvestigation(
params: GetInvestigationParams,
repository: InvestigationRepository
): Promise<GetInvestigationResponse> {
const investigation = await repository.findById(params.id);
const investigation = await repository.findById(params.investigationId);
return getInvestigationResponseSchema.encode(investigation);
}

View file

@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
GetInvestigationItemsResponse,
getInvestigationItemsResponseSchema,
} from '@kbn/investigation-shared';
import { InvestigationRepository } from './investigation_repository';
export async function getInvestigationItems(
investigationId: string,
repository: InvestigationRepository
): Promise<GetInvestigationItemsResponse> {
const investigation = await repository.findById(investigationId);
return getInvestigationItemsResponseSchema.encode(investigation.items);
}

View file

@ -6,10 +6,11 @@
*/
import { Logger, SavedObjectsClientContract } from '@kbn/core/server';
import { investigationSchema } from '@kbn/investigation-shared';
import { isLeft } from 'fp-ts/lib/Either';
import { Investigation, StoredInvestigation, investigationSchema } from '../models/investigation';
import { SO_INVESTIGATION_TYPE } from '../saved_objects/investigation';
import { Investigation, StoredInvestigation } from '../models/investigation';
import { Paginated, Pagination } from '../models/pagination';
import { SO_INVESTIGATION_TYPE } from '../saved_objects/investigation';
export interface InvestigationRepository {
save(investigation: Investigation): Promise<void>;

View file

@ -8,7 +8,7 @@
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import {
CreateInvestigationInput,
CreateInvestigationParams,
CreateInvestigationResponse,
FindInvestigationsResponse,
} from '@kbn/investigation-shared';
@ -26,7 +26,7 @@ export function useCreateInvestigation() {
return useMutation<
CreateInvestigationResponse,
ServerError,
{ investigation: CreateInvestigationInput },
{ investigation: CreateInvestigationParams },
{ previousData?: FindInvestigationsResponse; queryKey?: QueryKey }
>(
['createInvestigation'],