Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Use the new internal users API in the UI #150432

Merged
merged 36 commits into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0052783
Create endpoint
cnasikas Jan 25, 2023
dc76d81
Add integration tests
cnasikas Jan 27, 2023
da0b433
Fix types
cnasikas Jan 27, 2023
9e32bbf
Merge branch 'main' into user_actions_users_api
cnasikas Jan 30, 2023
c80ca5f
Fix SO test
cnasikas Jan 31, 2023
3cedccf
Change response structure
cnasikas Jan 31, 2023
1327690
Merge branch 'main' into user_actions_users_api
cnasikas Jan 31, 2023
1479e88
Fix imports
cnasikas Jan 31, 2023
1b2804e
Improvements
cnasikas Jan 31, 2023
74a1b5b
Tests for basic license
cnasikas Jan 31, 2023
5765a73
Merge branch 'main' into user_actions_users_api
cnasikas Jan 31, 2023
8d042ac
Merge branch 'main' into user_actions_users_api
cnasikas Feb 2, 2023
b69eb79
PR feedback
cnasikas Feb 2, 2023
68802de
Better structure
cnasikas Feb 3, 2023
8cf7876
PR feedback
cnasikas Feb 3, 2023
c02905d
Fix tests
cnasikas Feb 3, 2023
2163590
Merge branch 'main' into user_actions_users_api
cnasikas Feb 5, 2023
36d5548
Better naming
cnasikas Feb 5, 2023
39217c2
Create useGetCaseUsers hook
cnasikas Feb 5, 2023
4f1ffbb
Rename ElasticUser to CaseUser
cnasikas Feb 6, 2023
f1f1f1c
Merge branch 'main' into use_users_api_ui
cnasikas Feb 6, 2023
b3a9716
Merge branch 'main' into use_users_api_ui
cnasikas Feb 6, 2023
ca455b6
Change to the new API
cnasikas Feb 6, 2023
d943837
Fix tests & types
cnasikas Feb 7, 2023
d73d7fe
Merge branch 'main' into use_users_api_ui
cnasikas Feb 8, 2023
f8e07f9
Merge branch 'main' into use_users_api_ui
cnasikas Feb 9, 2023
e8193ff
Fix display name when rendering users
cnasikas Feb 9, 2023
377cd88
Add assignees as participants
cnasikas Feb 9, 2023
83331e4
Fallback reporter
cnasikas Feb 9, 2023
d8e94d8
Add unit tests
cnasikas Feb 9, 2023
a2ca4c6
Fix tests
cnasikas Feb 9, 2023
c9be240
Fix types
cnasikas Feb 9, 2023
1854fd8
PR feedback
cnasikas Feb 10, 2023
b7d8711
Sort user list
cnasikas Feb 10, 2023
e439622
Merge branch 'main' into use_users_api_ui
cnasikas Feb 11, 2023
3475370
Fix tests
cnasikas Feb 11, 2023
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
28 changes: 21 additions & 7 deletions x-pack/plugins/cases/common/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,37 @@

import * as rt from 'io-ts';

const UserWithoutProfileUidRt = rt.type({
email: rt.union([rt.undefined, rt.null, rt.string]),
full_name: rt.union([rt.undefined, rt.null, rt.string]),
username: rt.union([rt.undefined, rt.null, rt.string]),
});

export const UserRt = rt.intersection([
UserWithoutProfileUidRt,
rt.partial({ profile_uid: rt.string }),
]);

export const UserWithProfileInfoRt = rt.intersection([
rt.type({
email: rt.union([rt.undefined, rt.null, rt.string]),
full_name: rt.union([rt.undefined, rt.null, rt.string]),
username: rt.union([rt.undefined, rt.null, rt.string]),
user: UserWithoutProfileUidRt,
}),
rt.partial({ uid: rt.string }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a way we can tie this implementation with the security plugin 🤔 My concern is that if they make a breaking change to the response structure and make the changes throughout the code base, this won't show up as needing to be changed and it will break the UI.

Here's an idea, how about we add an integration test that does a bulk get using the security API and then does a decode using this schema? Or something like that, that way if there's a breaking change at least we'll get a failing test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing idea! I was thinking about the same but I could not find a good way to do it.

rt.partial({
avatar: rt.partial({ initials: rt.string, color: rt.string, imageUrl: rt.string }),
}),
rt.partial({ profile_uid: rt.string }),
]);

export const UsersRt = rt.array(UserRt);

export type User = rt.TypeOf<typeof UserRt>;
export type UserWithProfileInfo = rt.TypeOf<typeof UserWithProfileInfoRt>;

export const GetCaseUsersResponseRt = rt.type({
assignees: rt.array(UserRt),
unassignedUsers: rt.array(UserRt),
participants: rt.array(UserRt),
assignees: rt.array(UserWithProfileInfoRt),
unassignedUsers: rt.array(UserWithProfileInfoRt),
participants: rt.array(UserWithProfileInfoRt),
reporter: UserWithProfileInfoRt,
});

export type GetCaseUsersResponse = rt.TypeOf<typeof GetCaseUsersResponseRt>;
6 changes: 6 additions & 0 deletions x-pack/plugins/cases/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ type SnakeToCamelCaseString<S extends string> = S extends `${infer T}_${infer U}
? `${T}${Capitalize<SnakeToCamelCaseString<U>>}`
: S;

type SnakeToCamelCaseArray<T> = T extends Array<infer ArrayItem>
? Array<SnakeToCamelCase<ArrayItem>>
: T;

export type SnakeToCamelCase<T> = T extends Record<string, unknown>
? {
[K in keyof T as SnakeToCamelCaseString<K & string>]: SnakeToCamelCase<T[K]>;
}
: T extends unknown[]
? SnakeToCamelCaseArray<T>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extends the utility function to support arrays.

: T;

export enum CASE_VIEW_PAGE_TABS {
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/cases/common/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type {
CommentResponseExternalReferenceType,
CommentResponseTypePersistableState,
GetCaseConnectorsResponse,
GetCaseUsersResponse,
} from '../api';
import type { PUSH_CASES_CAPABILITY } from '../constants';
import type { SnakeToCamelCase } from '../types';
Expand Down Expand Up @@ -87,6 +88,7 @@ export type CasesStatus = SnakeToCamelCase<CasesStatusResponse>;
export type CasesMetrics = SnakeToCamelCase<CasesMetricsResponse>;
export type CaseUpdateRequest = SnakeToCamelCase<CasePatchRequest>;
export type CaseConnectors = SnakeToCamelCase<GetCaseConnectorsResponse>;
export type CaseUsers = GetCaseUsersResponse;

export interface ResolvedCase {
case: Case;
Expand Down Expand Up @@ -147,7 +149,7 @@ export enum SortFieldCase {
title = 'title',
}

export type ElasticUser = SnakeToCamelCase<User>;
export type CaseUser = SnakeToCamelCase<User>;

export interface FetchCasesProps extends ApiProps {
queryParams?: QueryParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { createAppMockRenderer } from '../../common/mock';
import '../../common/mock/match_media';
import { useCaseViewNavigation, useUrlParams } from '../../common/navigation/hooks';
import { useGetSupportedActionConnectors } from '../../containers/configure/use_get_supported_action_connectors';
import { basicCaseClosed, connectorsMock } from '../../containers/mock';
import { basicCaseClosed, connectorsMock, getCaseUsersMockResponse } from '../../containers/mock';
import type { UseGetCase } from '../../containers/use_get_case';
import { useGetCase } from '../../containers/use_get_case';
import { useGetCaseMetrics } from '../../containers/use_get_case_metrics';
Expand All @@ -24,7 +24,7 @@ import { useGetTags } from '../../containers/use_get_tags';
import { usePostPushToService } from '../../containers/use_post_push_to_service';
import { useGetCaseConnectors } from '../../containers/use_get_case_connectors';
import { useUpdateCase } from '../../containers/use_update_case';
import { useBulkGetUserProfiles } from '../../containers/user_profiles/use_bulk_get_user_profiles';
import { useGetCaseUsers } from '../../containers/use_get_case_users';
import { CaseViewPage } from './case_view_page';
import {
caseData,
Expand All @@ -49,6 +49,7 @@ jest.mock('../../containers/use_get_case');
jest.mock('../../containers/configure/use_get_supported_action_connectors');
jest.mock('../../containers/use_post_push_to_service');
jest.mock('../../containers/use_get_case_connectors');
jest.mock('../../containers/use_get_case_users');
jest.mock('../../containers/user_profiles/use_bulk_get_user_profiles');
jest.mock('../user_actions/timestamp', () => ({
UserActionTimestamp: () => <></>,
Expand All @@ -67,7 +68,7 @@ const usePostPushToServiceMock = usePostPushToService as jest.Mock;
const useGetCaseConnectorsMock = useGetCaseConnectors as jest.Mock;
const useGetCaseMetricsMock = useGetCaseMetrics as jest.Mock;
const useGetTagsMock = useGetTags as jest.Mock;
const useBulkGetUserProfilesMock = useBulkGetUserProfiles as jest.Mock;
const useGetCaseUsersMock = useGetCaseUsers as jest.Mock;

const mockGetCase = (props: Partial<UseGetCase> = {}) => {
const data = {
Expand Down Expand Up @@ -99,6 +100,7 @@ describe('CaseViewPage', () => {
const data = caseProps.caseData;
let appMockRenderer: AppMockRenderer;
const caseConnectors = getCaseConnectorsMockResponse();
const caseUsers = getCaseUsersMockResponse();

beforeEach(() => {
mockGetCase();
Expand All @@ -113,10 +115,11 @@ describe('CaseViewPage', () => {
});
useGetConnectorsMock.mockReturnValue({ data: connectorsMock, isLoading: false });
useGetTagsMock.mockReturnValue({ data: [], isLoading: false });
useBulkGetUserProfilesMock.mockReturnValue({ data: new Map(), isLoading: false });
const license = licensingMock.createLicense({
license: { type: 'platinum' },
});
useGetCaseUsersMock.mockReturnValue({ isLoading: false, data: caseUsers });

appMockRenderer = createAppMockRenderer({ license });
});

Expand Down
Loading