-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add profiles overview/edit pages (#17769)
- Loading branch information
1 parent
84001dc
commit 449e99f
Showing
52 changed files
with
2,330 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
type = "a" | ||
message = "Add index set field type profiles overview and edit page" | ||
|
||
pulls = ["17775"] | ||
issues=["17746"] | ||
|
||
details.user = """ | ||
Before this change, it was possible to create and manage custom field type mappings | ||
of existing index sets. For every new index set that is created, | ||
these steps have to be repeated though. This change gives an option to bundle up | ||
custom field types into profiles. | ||
""" |
124 changes: 124 additions & 0 deletions
124
...og2-web-interface/src/components/indices/IndexSetFieldTypeProfiles/CreateProfile.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
import * as React from 'react'; | ||
import { render, screen, fireEvent, act, waitFor } from 'wrappedTestingLibrary'; | ||
import selectEvent from 'react-select-event'; | ||
|
||
import asMock from 'helpers/mocking/AsMock'; | ||
import { loadViewsPlugin, unloadViewsPlugin } from 'views/test/testViewsPlugin'; | ||
import useFieldTypesForMappings from 'views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypesForMappings'; | ||
import useFieldTypes from 'views/logic/fieldtypes/useFieldTypes'; | ||
import CreateProfile from 'components/indices/IndexSetFieldTypeProfiles/CreateProfile'; | ||
import useProfileMutations from 'components/indices/IndexSetFieldTypeProfiles/hooks/useProfileMutations'; | ||
import { simpleFields } from 'fixtures/fields'; | ||
|
||
const renderCreateNewProfile = () => render( | ||
<CreateProfile />, | ||
); | ||
|
||
jest.mock('components/indices/IndexSetFieldTypeProfiles/hooks/useProfileMutations', () => jest.fn()); | ||
jest.mock('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypesForMappings', () => jest.fn()); | ||
|
||
jest.mock('views/logic/fieldtypes/useFieldTypes', () => jest.fn()); | ||
|
||
const selectItem = async (select: HTMLElement, option: string | RegExp) => { | ||
selectEvent.openMenu(select); | ||
|
||
return selectEvent.select(select, option); | ||
}; | ||
|
||
describe('IndexSetFieldTypesList', () => { | ||
const createMock = jest.fn(() => Promise.resolve()); | ||
const editMock = jest.fn(() => Promise.resolve()); | ||
|
||
beforeAll(loadViewsPlugin); | ||
|
||
afterAll(unloadViewsPlugin); | ||
|
||
beforeEach(() => { | ||
asMock(useFieldTypesForMappings).mockReturnValue({ | ||
data: { | ||
fieldTypes: { | ||
string: 'String type', | ||
int: 'Number(int)', | ||
bool: 'Boolean', | ||
ip: 'IP', | ||
date: 'Date', | ||
}, | ||
}, | ||
isLoading: false, | ||
}); | ||
|
||
asMock(useProfileMutations).mockReturnValue(({ | ||
editProfile: editMock, | ||
isEditLoading: false, | ||
createProfile: createMock, | ||
isCreateLoading: false, | ||
isLoading: false, | ||
})); | ||
|
||
asMock(useFieldTypes).mockImplementation(() => ( | ||
{ data: simpleFields().toArray(), refetch: jest.fn() } | ||
)); | ||
}); | ||
|
||
it('Run createProfile with form data', async () => { | ||
renderCreateNewProfile(); | ||
|
||
const name = await screen.findByRole('textbox', { | ||
name: /name/i, | ||
hidden: true, | ||
}); | ||
const description = await screen.findByRole('textbox', { | ||
name: /description/i, | ||
hidden: true, | ||
}); | ||
const addMappingButton = await screen.findByRole('button', { name: /add mapping/i }); | ||
|
||
// eslint-disable-next-line testing-library/no-unnecessary-act | ||
await act(async () => { | ||
fireEvent.click(addMappingButton); | ||
}); | ||
|
||
const fieldFirst = await screen.findByLabelText(/select customFieldMappings.0.field/i); | ||
const typeFirst = await screen.findByLabelText(/select customFieldMappings.0.type/i); | ||
const fieldSecond = await screen.findByLabelText(/select customFieldMappings.1.field/i); | ||
const typeSecond = await screen.findByLabelText(/select customFieldMappings.1.type/i); | ||
const submitButton = await screen.findByTitle(/create profile/i); | ||
|
||
// eslint-disable-next-line testing-library/no-unnecessary-act | ||
await act(async () => { | ||
fireEvent.change(name, { target: { value: 'Profile new' } }); | ||
fireEvent.change(description, { target: { value: 'Profile description' } }); | ||
await selectItem(fieldFirst, 'date'); | ||
await selectItem(typeFirst, 'String type'); | ||
await selectItem(fieldSecond, 'http_method'); | ||
await selectItem(typeSecond, 'String type'); | ||
await waitFor(() => expect(submitButton.hasAttribute('disabled')).toBe(false)); | ||
fireEvent.click(submitButton); | ||
}); | ||
|
||
expect(createMock).toHaveBeenCalledWith({ | ||
name: 'Profile new', | ||
description: 'Profile description', | ||
customFieldMappings: [ | ||
{ field: 'date', type: 'string' }, | ||
{ field: 'http_method', type: 'string' }, | ||
], | ||
}); | ||
}); | ||
}); |
61 changes: 61 additions & 0 deletions
61
graylog2-web-interface/src/components/indices/IndexSetFieldTypeProfiles/CreateProfile.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
import React, { useMemo, useCallback, useEffect } from 'react'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
import useSendTelemetry from 'logic/telemetry/useSendTelemetry'; | ||
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants'; | ||
import { getPathnameWithoutId } from 'util/URLUtils'; | ||
import useLocation from 'routing/useLocation'; | ||
import ProfileForm from 'components/indices/IndexSetFieldTypeProfiles/ProfileForm'; | ||
import type { IndexSetFieldTypeProfile } from 'components/indices/IndexSetFieldTypeProfiles/types'; | ||
import useProfileMutations from 'components/indices/IndexSetFieldTypeProfiles/hooks/useProfileMutations'; | ||
import Routes from 'routing/Routes'; | ||
|
||
const CreateProfile = () => { | ||
const sendTelemetry = useSendTelemetry(); | ||
const { pathname } = useLocation(); | ||
const navigate = useNavigate(); | ||
const { createProfile } = useProfileMutations(); | ||
const telemetryPathName = useMemo(() => getPathnameWithoutId(pathname), [pathname]); | ||
|
||
const onSubmit = useCallback((profile: IndexSetFieldTypeProfile) => { | ||
createProfile(profile).then(() => { | ||
sendTelemetry(TELEMETRY_EVENT_TYPE.INDEX_SET_FIELD_TYPE_PROFILE.CREATED, { | ||
app_pathname: telemetryPathName, | ||
app_action_value: { mappingsQuantity: profile?.customFieldMappings?.length }, | ||
}); | ||
|
||
navigate(Routes.SYSTEM.INDICES.FIELD_TYPE_PROFILES.OVERVIEW); | ||
}); | ||
}, [createProfile, navigate, sendTelemetry, telemetryPathName]); | ||
|
||
useEffect(() => { | ||
sendTelemetry(TELEMETRY_EVENT_TYPE.INDEX_SET_FIELD_TYPE_PROFILE.NEW_OPENED, { app_pathname: telemetryPathName, app_action_value: 'create-new-index-set-field-type-profile-opened' }); | ||
}, [sendTelemetry, telemetryPathName]); | ||
|
||
const onCancel = useCallback(() => { | ||
sendTelemetry(TELEMETRY_EVENT_TYPE.INDEX_SET_FIELD_TYPE_PROFILE.NEW_CANCELED, { app_pathname: telemetryPathName, app_action_value: 'create-new-index-set-field-type-profile-canceled' }); | ||
navigate(Routes.SYSTEM.INDICES.FIELD_TYPE_PROFILES.OVERVIEW); | ||
}, [navigate, sendTelemetry, telemetryPathName]); | ||
|
||
return ( | ||
<ProfileForm onCancel={onCancel} submitButtonText="Create profile" submitLoadingText="Creating profile..." onSubmit={onSubmit} /> | ||
); | ||
}; | ||
|
||
export default CreateProfile; |
30 changes: 30 additions & 0 deletions
30
...g2-web-interface/src/components/indices/IndexSetFieldTypeProfiles/CreateProfileButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
|
||
import * as React from 'react'; | ||
|
||
import { Button } from 'components/bootstrap'; | ||
import Routes from 'routing/Routes'; | ||
import { LinkContainer } from 'components/common/router'; | ||
|
||
const CreateProfileButton = () => ( | ||
<LinkContainer to={Routes.SYSTEM.INDICES.FIELD_TYPE_PROFILES.CREATE}> | ||
<Button bsStyle="success">Create profile</Button> | ||
</LinkContainer> | ||
); | ||
|
||
export default CreateProfileButton; |
44 changes: 44 additions & 0 deletions
44
...2-web-interface/src/components/indices/IndexSetFieldTypeProfiles/CustomFieldTypesList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright (C) 2020 Graylog, Inc. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the Server Side Public License, version 1, | ||
* as published by MongoDB, Inc. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* Server Side Public License for more details. | ||
* | ||
* You should have received a copy of the Server Side Public License | ||
* along with this program. If not, see | ||
* <http://www.mongodb.com/licensing/server-side-public-license>. | ||
*/ | ||
import React from 'react'; | ||
import { styled } from 'styled-components'; | ||
|
||
import type { CustomFieldMapping } from 'components/indices/IndexSetFieldTypeProfiles/types'; | ||
import type { FieldTypes } from 'views/logic/fieldactions/ChangeFieldType/types'; | ||
|
||
const Item = styled.div` | ||
display: flex; | ||
gap: 5px; | ||
flex-wrap: wrap; | ||
`; | ||
|
||
const List = styled.div` | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
gap: 1em; | ||
`; | ||
const CustomFieldTypesList = ({ list, fieldTypes }: { list: Array<CustomFieldMapping>, fieldTypes: FieldTypes }) => ( | ||
<List> | ||
{list.map(({ field, type }) => ( | ||
<Item key={field}> | ||
<b>{field}:</b><i>{fieldTypes[type]}</i> | ||
</Item> | ||
))} | ||
</List> | ||
); | ||
|
||
export default CustomFieldTypesList; |
Oops, something went wrong.