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

[Maps] Add shape drawing wizard- creates index only #96913

Closed
wants to merge 70 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
5f8aa18
Placeholder feature edit control in place
Mar 26, 2021
2cbd6b0
Merge remote-tracking branch 'upstream/master' into shape-drawing-ui
Mar 31, 2021
a80038a
Placeholder menu added
Mar 31, 2021
26ec7bf
Redux integrated
Apr 1, 2021
8ca809a
Single shape drawing
Apr 1, 2021
f9eeb95
Merge remote-tracking branch 'upstream/master' into shape-drawing-ui
Apr 1, 2021
b8eea96
Keep draw mode active
Apr 2, 2021
b4f3af0
Trigger editing through add layer wizard
Apr 5, 2021
001c87f
Add index checking utils
Apr 5, 2021
95f8f14
Connect error checking
Apr 5, 2021
2de30ec
Conditionally show edit toolbar
Apr 5, 2021
92e17aa
Connect missing shapes. Some clean up
Apr 5, 2021
143b24a
Track features and index name in store
Apr 5, 2021
e129539
Connect add layer button enablement
Apr 6, 2021
9c5ca16
Connect persistence utils
Apr 6, 2021
423e3aa
Dispatch clearing action
Apr 6, 2021
f238ccb
Full roundtrip w/ settings showing. Moved async calls to component
Apr 6, 2021
d450fd1
Adjust addLayer call for async
Apr 7, 2021
3d7ad76
Merge remote-tracking branch 'upstream/master' into shape-drawing-ui
Apr 7, 2021
b12ad85
Add edit controls
Apr 7, 2021
576b3d3
Fix deleting functionality to update store
Apr 7, 2021
cd211f9
Add toggle color to buttons. Fall back to edit between drawings
Apr 8, 2021
4f79738
Deselect features still selected when entering trash mode
Apr 8, 2021
ceae6d2
Add geometry check to filter out invalid geometries
Apr 8, 2021
bee0fd5
Clear out old drawing data on cancel. Update action names
Apr 8, 2021
3725859
Add circle drawing button
Apr 8, 2021
5200165
Add tools show on map with cancel. Not completely connected to add la…
Apr 12, 2021
2cb1832
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 12, 2021
4ab4c54
Controls and feature handling almost entirely removed. Index creation…
Apr 12, 2021
32e4dcf
Remove more unused files related to draw toolbar
Apr 12, 2021
9579d44
Fix step handling
Apr 12, 2021
da58916
Remove toolbar logic
Apr 12, 2021
28ffc5f
Add same disabling logic used by file upload
Apr 12, 2021
2217bc1
Put wizard behind feature flag
Apr 13, 2021
fa6fd2e
More clean up
Apr 13, 2021
7d2d5a4
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 13, 2021
634ede6
Remove unused source
Apr 13, 2021
a549a1e
Clean up, type fixes
Apr 13, 2021
5bcd3a9
Remove unused constant
Apr 13, 2021
5035bc5
Clean up unused feature actions. Misc. clean up
Apr 13, 2021
5e39efb
Fix i18n
Apr 14, 2021
7cd1482
eslint fix
Apr 14, 2021
958cc6a
Adding toolbar icons and draw layer icon
miukimiu Apr 14, 2021
d3c064d
Merge branch 'shape-drawing-wizard' of https://github.com/aaronjcaldw…
miukimiu Apr 14, 2021
9529b94
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 14, 2021
177d5d2
TS conversions
Apr 15, 2021
65257a0
Review feedback. Lots of clean up
Apr 15, 2021
69e9393
Update card description
Apr 15, 2021
78c3721
Merge branch 'shape-drawing-wizard' of github.com:aaronjcaldwell/kiba…
Apr 15, 2021
9c817f7
Linting errors
Apr 15, 2021
431890a
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 20, 2021
a5b1b1d
Review feedback. Update layer card title and description
Apr 20, 2021
087861a
Review feedback. Update wording on index name entry page
Apr 20, 2021
e2b19c2
Update x-pack/plugins/maps/public/classes/layers/new_vector_layer_wiz…
kindsun Apr 20, 2021
09017eb
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 26, 2021
2dd166d
Integrate file upload index name component
Apr 27, 2021
bc75830
Revise index calls to use kibana https fetch. Remove http service
Apr 27, 2021
4162876
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 28, 2021
b25bcae
Review feedback. Mostly clean up
Apr 29, 2021
73f2536
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
Apr 30, 2021
58c84e8
Make index name validation callback props optional
May 3, 2021
bd783ad
Review feedback. Async checks, revised async component init, clean up
May 3, 2021
8799716
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
May 3, 2021
7706cca
More clean up around async component init handling
May 3, 2021
f02a8d4
Review feedback. Handle async component load on file upload side
May 5, 2021
6107902
Update maps side to not use async in component load
May 5, 2021
4f09cd8
Merge remote-tracking branch 'upstream/master' into shape-drawing-wizard
May 5, 2021
065f745
Review feedback. Move button enable/disable out of callback
May 5, 2021
e4c1c92
Review feedback. Move wrapper components to api dir
May 5, 2021
0d8c253
Add semi-colon
May 5, 2021
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
22 changes: 8 additions & 14 deletions x-pack/plugins/file_upload/public/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
* 2.0.
*/

import React from 'react';
import { FileUploadComponentProps, lazyLoadModules } from '../lazy_load_bundle';
import { lazyLoadModules } from '../lazy_load_bundle';
import type { IImporter, ImportFactoryOptions } from '../importer';
import { IndexNameFormProps } from '../';
import type { HasImportPermission, FindFileStructureResponse } from '../../common';
import type { getMaxBytes, getMaxBytesFormatted } from '../importer/get_max_bytes';
import { JsonUploadAndParseAsyncWrapper } from './json_upload_and_parse_async_wrapper';
import { IndexNameFormAsyncWrapper } from './index_name_form_async_wrapper';

export interface FileUploadStartApi {
getFileUploadComponent(): ReturnType<typeof getFileUploadComponent>;
thomasneirynck marked this conversation as resolved.
Show resolved Hide resolved
getIndexNameFormComponent(): Promise<React.ComponentType<IndexNameFormProps>>;
getIndexNameFormComponent(): ReturnType<typeof getIndexNameFormComponent>;
thomasneirynck marked this conversation as resolved.
Show resolved Hide resolved
importerFactory: typeof importerFactory;
getMaxBytes: typeof getMaxBytes;
getMaxBytesFormatted: typeof getMaxBytesFormatted;
Expand All @@ -30,18 +30,12 @@ export interface GetTimeFieldRangeResponse {
end: { epoch: number; string: string };
}

export async function getFileUploadComponent(): Promise<
React.ComponentType<FileUploadComponentProps>
> {
const fileUploadModules = await lazyLoadModules();
return fileUploadModules.JsonUploadAndParse;
export function getFileUploadComponent() {
return JsonUploadAndParseAsyncWrapper;
}

export async function getIndexNameFormComponent(): Promise<
React.ComponentType<IndexNameFormProps>
> {
const fileUploadModules = await lazyLoadModules();
return fileUploadModules.IndexNameForm;
export function getIndexNameFormComponent() {
return IndexNameFormAsyncWrapper;
}

export async function importerFactory(
Expand Down
Original file line number Diff line number Diff line change
@@ -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 React from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
import { lazyLoadModules } from '../lazy_load_bundle';
import { IndexNameFormProps } from '../index';

interface State {
IndexNameForm: React.ComponentType<IndexNameFormProps> | null;
}

export class IndexNameFormAsyncWrapper extends React.Component<IndexNameFormProps, State> {
state: State = {
IndexNameForm: null,
};

componentDidMount() {
lazyLoadModules().then((modules) => {
this.setState({
IndexNameForm: modules.IndexNameForm,
});
});
}

render() {
const { IndexNameForm } = this.state;
return IndexNameForm ? <IndexNameForm {...this.props} /> : <EuiLoadingSpinner />;
thomasneirynck marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 React from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
import { FileUploadComponentProps, lazyLoadModules } from '../lazy_load_bundle';

interface State {
JsonUploadAndParse: React.ComponentType<FileUploadComponentProps> | null;
}

export class JsonUploadAndParseAsyncWrapper extends React.Component<
FileUploadComponentProps,
State
> {
state: State = {
JsonUploadAndParse: null,
};

componentDidMount() {
lazyLoadModules().then((modules) => {
this.setState({
JsonUploadAndParse: modules.JsonUploadAndParse,
});
});
}

render() {
const { JsonUploadAndParse } = this.state;
return JsonUploadAndParse ? <JsonUploadAndParse {...this.props} /> : <EuiLoadingSpinner />;
thomasneirynck marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export interface Props {
indexName: string;
indexNameError?: string;
onIndexNameChange: (name: string, error?: string) => void;
onIndexNameValidationStart: () => void;
onIndexNameValidationEnd: () => void;
onIndexNameValidationStart?: () => void;
thomasneirynck marked this conversation as resolved.
Show resolved Hide resolved
onIndexNameValidationEnd?: () => void;
}

export class IndexNameForm extends Component<Props> {
Expand All @@ -34,15 +34,19 @@ export class IndexNameForm extends Component<Props> {
const indexName = event.target.value;
this.props.onIndexNameChange(indexName);
this._validateIndexName(indexName);
this.props.onIndexNameValidationStart();
if (this.props.onIndexNameValidationStart) {
this.props.onIndexNameValidationStart();
}
};

_validateIndexName = _.debounce(async (indexName: string) => {
const indexNameError = await validateIndexName(indexName);
if (!this._isMounted || indexName !== this.props.indexName) {
return;
}
this.props.onIndexNameValidationEnd();
if (this.props.onIndexNameValidationEnd) {
this.props.onIndexNameValidationEnd();
}
this.props.onIndexNameChange(indexName, indexNameError);
}, 500);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface FileUploadComponentProps {

let loadModulesPromise: Promise<LazyLoadedFileUploadModules>;

interface LazyLoadedFileUploadModules {
export interface LazyLoadedFileUploadModules {
JsonUploadAndParse: React.ComponentType<FileUploadComponentProps>;
IndexNameForm: React.ComponentType<IndexNameFormProps>;
importerFactory: (format: string, options: ImportFactoryOptions) => IImporter | undefined;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const GIS_API_PATH = `api/${APP_ID}`;
export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`;
export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`;
export const INDEX_SOURCE_API_PATH = `${GIS_API_PATH}/docSource`;
export const INDEX_FEATURE_PATH = `/${GIS_API_PATH}/feature`;
kindsun marked this conversation as resolved.
Show resolved Hide resolved
export const API_ROOT_PATH = `/${GIS_API_PATH}`;

export const MVT_GETTILE_API_PATH = 'mvt/getTile';
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

export interface CreateDocSourceResp {
indexPatternId?: string;
success: boolean;
error?: Error;
}
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/maps/public/actions/map_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { AnyAction, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import turfBboxPolygon from '@turf/bbox-polygon';
import turfBooleanContains from '@turf/boolean-contains';

import { Filter, Query, TimeRange } from 'src/plugins/data/public';
import { MapStoreState } from '../reducers/store';
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import React, { Component } from 'react';
import { FeatureCollection } from 'geojson';
import { EuiPanel } from '@elastic/eui';
import { DEFAULT_MAX_RESULT_WINDOW, SCALING_TYPES } from '../../../../common/constants';
import { getFileUpload } from '../../../kibana_services';
import { GeoJsonFileSource } from '../../sources/geojson_file_source';
import { VectorLayer } from '../../layers/vector_layer';
import { createDefaultLayerDescriptor } from '../../sources/es_search_source';
import { RenderWizardArguments } from '../../layers/layer_wizard_registry';
import { FileUploadComponentProps, FileUploadGeoResults } from '../../../../../file_upload/public';
import { FileUploadGeoResults } from '../../../../../file_upload/public';
import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/public';
import { getFileUploadComponent } from '../../../kibana_services';

export enum UPLOAD_STEPS {
CONFIGURE_UPLOAD = 'CONFIGURE_UPLOAD',
Expand All @@ -34,7 +34,6 @@ enum INDEXING_STAGE {

interface State {
indexingStage: INDEXING_STAGE;
fileUploadComponent: React.ComponentType<FileUploadComponentProps> | null;
results?: FileUploadGeoResults;
}

Expand All @@ -43,12 +42,10 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument

state: State = {
indexingStage: INDEXING_STAGE.CONFIGURE,
fileUploadComponent: null,
};

componentDidMount() {
this._isMounted = true;
this._loadFileUploadComponent();
}

componentWillUnmount() {
Expand Down Expand Up @@ -91,13 +88,6 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
this.props.advanceToNextStep();
});

async _loadFileUploadComponent() {
const fileUploadComponent = await getFileUpload().getFileUploadComponent();
if (this._isMounted) {
this.setState({ fileUploadComponent });
}
}

_onFileSelect = (geojsonFile: FeatureCollection, name: string, previewCoverage: number) => {
if (!this._isMounted) {
return;
Expand Down Expand Up @@ -157,11 +147,8 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
};

render() {
if (!this.state.fileUploadComponent) {
return null;
}
const FileUpload = getFileUploadComponent();

const FileUpload = this.state.fileUploadComponent;
return (
<EuiPanel>
<FileUpload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 React, { FunctionComponent } from 'react';

export const DrawLayerIcon: FunctionComponent = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="49"
height="25"
fill="none"
viewBox="0 0 49 25"
className="mapLayersWizardIcon"
>
<path
className="mapLayersWizardIcon__background"
d="M12.281 3l-6.625 7.625 1.657 8.938 35.218-.813v-13l-10.625-3.5-9.781 9.5L12.281 3z"
/>
<path
className="mapLayersWizardIcon__highlight"
fillRule="evenodd"
d="M31.775 1.68l11.256 3.708v13.85l-36.133.834-1.777-9.593 7.114-8.189 9.875 8.778 9.665-9.388zm.262 1.14l-9.897 9.612-9.813-8.722-6.135 7.06 1.535 8.283 34.304-.792V6.111L32.037 2.82z"
clipRule="evenodd"
/>
<circle cx="7.281" cy="19.5" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="5.656" cy="10.25" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="12.156" cy="3.625" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="22" cy="11.6" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="31.969" cy="2.5" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="42.344" cy="6.125" r="2.5" className="mapLayersWizardIcon__highlight" />
<circle cx="42.344" cy="19" r="2.5" className="mapLayersWizardIcon__highlight" />
</svg>
);
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { mvtVectorSourceWizardConfig } from '../sources/mvt_single_layer_vector_
import { ObservabilityLayerWizardConfig } from './solution_layers/observability';
import { SecurityLayerWizardConfig } from './solution_layers/security';
import { choroplethLayerWizardConfig } from './choropleth_layer_wizard';
import { newVectorLayerWizardConfig } from './new_vector_layer_wizard';
import { getMapAppConfig } from '../../kibana_services';

let registered = false;
export function registerLayerWizards() {
Expand All @@ -39,6 +41,9 @@ export function registerLayerWizards() {

// Registration order determines display order
registerLayerWizard(uploadLayerWizardConfig);
if (getMapAppConfig().enableDrawingFeature) {
registerLayerWizard(newVectorLayerWizardConfig);
}
registerLayerWizard(esDocumentsLayerWizardConfig);
// @ts-ignore
registerLayerWizard(choroplethLayerWizardConfig);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 { i18n } from '@kbn/i18n';
import React from 'react';
import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry';
import { NewVectorLayerEditor } from './wizard';
import { DrawLayerIcon } from '../../layers/icons/draw_layer_icon';
import { getFileUpload } from '../../../kibana_services';
import { LAYER_WIZARD_CATEGORY } from '../../../../common';

const ADD_VECTOR_DRAWING_LAYER = 'ADD_VECTOR_DRAWING_LAYER';

export const newVectorLayerWizardConfig: LayerWizard = {
categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH],
description: i18n.translate('xpack.maps.newVectorLayerWizard.description', {
defaultMessage: 'Draw points, lines, and polygons to create or edit documents',
Copy link
Contributor

Choose a reason for hiding this comment

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

my 2c the wording should be more generic, but @kmartastic has final call here:

more along the lines of:

title: Create new document layer

and

description: ```Creates a new index pattern. Can be used to draw shapes and points`.

or something.

We can work on the more integrated wording, when we actually integrate the flow after this PR and the one for the drawing toolbar have merged. But right now, that integration is still todo, so it's a little confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see your point, just a little reluctant to touch the wording again on this PR after we got to somewhat of a settling point. I think we'll be iterating on this before pulling out of experimental mode so no need for it to be final at this point. Let me know if you feel strongly though

Copy link
Contributor

Choose a reason for hiding this comment

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

let's change it, so this PR can standalone and we're not priming it for hypothetical later work.

I'd propose:

  • "title": "Create new document layer"
  • "description": Creates a new empty index pattern. Use this to draw shapes and point".

^ we can change once we start pulling threads together.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we keep the title shorter? Most of the other card titles are a single word. I really like "draw" so something short.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 to a shorter title, which makes it easier to scan.

For the description how about shortening to:

Create an empty index pattern to draw shapes and points.

Also, aren't index patterns being renamed in 7.14?

}),
disabledReason: i18n.translate('xpack.maps.newVectorLayerWizard.disabledDesc', {
defaultMessage:
'Unable to draw vector shapes, you are missing the Kibana privilege "Index Pattern Management".',
}),
getIsDisabled: async () => {
const hasImportPermission = await getFileUpload().hasImportPermission({
checkCreateIndexPattern: true,
checkHasManagePipeline: false,
});
return !hasImportPermission;
},
icon: DrawLayerIcon,
prerequisiteSteps: [
{
id: ADD_VECTOR_DRAWING_LAYER,
label: i18n.translate('xpack.maps.newVectorLayerWizard.indexNewLayer', {
defaultMessage: 'Index new layer',
}),
},
],
renderWizard: (renderWizardArguments: RenderWizardArguments) => {
return <NewVectorLayerEditor {...renderWizardArguments} />;
},
title: i18n.translate('xpack.maps.newVectorLayerWizard.title', {
defaultMessage: 'Draw',
}),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { newVectorLayerWizardConfig } from './config';
Original file line number Diff line number Diff line change
@@ -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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { getHttp } from '../../../../kibana_services';
import { CreateDocSourceResp, INDEX_SOURCE_API_PATH } from '../../../../../common';

export const createNewIndexAndPattern = async (indexName: string) => {
return await getHttp().fetch<CreateDocSourceResp>({
path: `/${INDEX_SOURCE_API_PATH}`,
method: 'POST',
body: convertObjectToBlob({
index: indexName,
// Initially set to static mappings
mappings: {
properties: {
coordinates: {
type: 'geo_shape',
},
},
},
}),
});
};

const convertObjectToBlob = (obj: unknown) => {
return new Blob([JSON.stringify(obj)], { type: 'application/json' });
};
Loading