Skip to content

Commit

Permalink
[Maps] direct Discover "visualize" to open Maps application (#58549) (#…
Browse files Browse the repository at this point in the history
…59066)

* [Maps] direct Discover visualize to Maps application

* pass initial layers to maps app

* add functional test

* fix parentheses messed up by lint fix

* fix i18n expression

* move logic into lib

* fix typescript errors

* use constant for geo_point and geo_shape, more TS noise

* use encode_array in an attempt to make TS happy

* another round of TS changes

* one more thing

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
nreese and elasticmachine committed Mar 2, 2020
1 parent e13b84a commit a32ffd5
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
syncStates,
BaseStateContainer,
} from '../../../../../../../plugins/kibana_utils/public';
import { esFilters, FilterManager, Filter } from '../../../../../../../plugins/data/public';
import { esFilters, FilterManager, Filter, Query } from '../../../../../../../plugins/data/public';

interface AppState {
export interface AppState {
/**
* Columns displayed in the table, cannot be changed by UI, just in discover's main app
*/
Expand All @@ -47,6 +47,7 @@ interface AppState {
* Number of records to be fetched after the anchor records (older records)
*/
successorCount: number;
query?: Query;
}

interface GlobalState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import './discover_field';
import './discover_field_search_directive';
import './discover_index_pattern_directive';
import fieldChooserTemplate from './field_chooser.html';
import { IndexPatternFieldList } from '../../../../../../../../plugins/data/public';
import {
IndexPatternFieldList,
KBN_FIELD_TYPES,
} from '../../../../../../../../plugins/data/public';
import { getMapsAppUrl, isFieldVisualizable, isMapsAppRegistered } from './lib/visualize_url_utils';

export function createFieldChooserDirective($location, config, $route) {
return {
Expand Down Expand Up @@ -186,8 +190,15 @@ export function createFieldChooserDirective($location, config, $route) {
return '';
}

if (
(field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) &&
isMapsAppRegistered()
) {
return getMapsAppUrl(field, $scope.indexPattern, $scope.state, $scope.columns);
}

let agg = {};
const isGeoPoint = field.type === 'geo_point';
const isGeoPoint = field.type === KBN_FIELD_TYPES.GEO_POINT;
const type = isGeoPoint ? 'tile_map' : 'histogram';
// If we're visualizing a date field, and our index is time based (and thus has a time filter),
// then run a date histogram
Expand Down Expand Up @@ -243,7 +254,7 @@ export function createFieldChooserDirective($location, config, $route) {
$scope.computeDetails = function(field, recompute) {
if (_.isUndefined(field.details) || recompute) {
field.details = {
visualizeUrl: field.visualizable ? getVisualizeUrl(field) : null,
visualizeUrl: isFieldVisualizable(field) ? getVisualizeUrl(field) : null,
...fieldCalculator.getFieldValueCounts({
hits: $scope.hits,
field: field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@
</div>
<string-field-progress-bar
percent="{{bucket.percent}}"
count="{{::bucket.count}}"
count="{{::bucket.count}}"
></string-field-progress-bar>
</div>
</div>
</div>

<a
ng-href="{{field.details.visualizeUrl}}"
ng-show="field.visualizable && canVisualize"
ng-show="field.details.visualizeUrl && canVisualize"
class="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall"
data-test-subj="fieldVisualize-{{::field.name}}"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import uuid from 'uuid/v4';
// @ts-ignore
import rison from 'rison-node';
import {
IFieldType,
IIndexPattern,
KBN_FIELD_TYPES,
} from '../../../../../../../../../plugins/data/public';
import { AppState } from '../../../angular/context_state';
import { getServices } from '../../../../kibana_services';

function getMapsAppBaseUrl() {
const mapsAppVisAlias = getServices()
.visualizations.types.getAliases()
.find(({ name }) => {
return name === 'maps';
});
return mapsAppVisAlias ? mapsAppVisAlias.aliasUrl : null;
}

export function isMapsAppRegistered() {
return getServices()
.visualizations.types.getAliases()
.some(({ name }) => {
return name === 'maps';
});
}

export function isFieldVisualizable(field: IFieldType) {
if (
(field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) &&
isMapsAppRegistered()
) {
return true;
}
return field.visualizable;
}

export function getMapsAppUrl(
field: IFieldType,
indexPattern: IIndexPattern,
appState: AppState,
columns: string[]
) {
const mapAppParams = new URLSearchParams();

// Copy global state
const locationSplit = window.location.href.split('discover?');
if (locationSplit.length > 1) {
const discoverParams = new URLSearchParams(locationSplit[1]);
const globalStateUrlValue = discoverParams.get('_g');
if (globalStateUrlValue) {
mapAppParams.set('_g', globalStateUrlValue);
}
}

// Copy filters and query in app state
const mapsAppState: any = {
filters: appState.filters || [],
};
if (appState.query) {
mapsAppState.query = appState.query;
}
// @ts-ignore
mapAppParams.set('_a', rison.encode(mapsAppState));

// create initial layer descriptor
const hasColumns = columns && columns.length && columns[0] !== '_source';
mapAppParams.set(
'initialLayers',
// @ts-ignore
rison.encode_array([
{
id: uuid(),
label: indexPattern.title,
sourceDescriptor: {
id: uuid(),
type: 'ES_SEARCH',
geoField: field.name,
tooltipProperties: hasColumns ? columns : [],
indexPatternId: indexPattern.id,
},
visible: true,
type: 'VECTOR',
},
])
);

return getServices().addBasePath(`${getMapsAppBaseUrl()}?${mapAppParams.toString()}`);
}
6 changes: 0 additions & 6 deletions test/functional/page_objects/discover_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,6 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
return await testSubjects.getVisibleText('discoverQueryHits');
}

async query(queryString) {
await find.setValue('input[aria-label="Search input"]', queryString);
await find.clickByCssSelector('button[aria-label="Search"]');
await PageObjects.header.waitUntilLoadingHasFinished();
}

async getDocHeader() {
const header = await find.byCssSelector('thead > tr:nth-child(1)');
return await header.getVisibleText();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { EMSTMSSource } from '../layers/sources/ems_tms_source';
import chrome from 'ui/chrome';
import { getKibanaTileMap } from '../meta';

export function getInitialLayers(layerListJSON) {
export function getInitialLayers(layerListJSON, initialLayers = []) {
if (layerListJSON) {
return JSON.parse(layerListJSON);
}
Expand All @@ -19,16 +19,16 @@ export function getInitialLayers(layerListJSON) {
const sourceDescriptor = KibanaTilemapSource.createDescriptor();
const source = new KibanaTilemapSource(sourceDescriptor);
const layer = source.createDefaultLayer();
return [layer.toLayerDescriptor()];
return [layer.toLayerDescriptor(), ...initialLayers];
}

const isEmsEnabled = chrome.getInjected('isEmsEnabled', true);
if (isEmsEnabled) {
const descriptor = EMSTMSSource.createDescriptor({ isAutoSelect: true });
const source = new EMSTMSSource(descriptor);
const layer = source.createDefaultLayer();
return [layer.toLayerDescriptor()];
return [layer.toLayerDescriptor(), ...initialLayers];
}

return [];
return initialLayers;
}
29 changes: 28 additions & 1 deletion x-pack/legacy/plugins/maps/public/angular/map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import _ from 'lodash';
import chrome from 'ui/chrome';
import rison from 'rison-node';
import 'ui/directives/listen';
import 'ui/directives/storage';
import React from 'react';
Expand Down Expand Up @@ -66,6 +67,32 @@ const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root';

const app = uiModules.get(MAP_APP_PATH, []);

function getInitialLayersFromUrlParam() {
const locationSplit = window.location.href.split('?');
if (locationSplit.length <= 1) {
return [];
}
const mapAppParams = new URLSearchParams(locationSplit[1]);
if (!mapAppParams.has('initialLayers')) {
return [];
}

try {
return rison.decode_array(mapAppParams.get('initialLayers'));
} catch (e) {
toastNotifications.addWarning({
title: i18n.translate('xpack.maps.initialLayers.unableToParseTitle', {
defaultMessage: `Inital layers not added to map`,
}),
text: i18n.translate('xpack.maps.initialLayers.unableToParseMessage', {
defaultMessage: `Unable to parse contents of 'initialLayers' parameter. Error: {errorMsg}`,
values: { errorMsg: e.message },
}),
});
return [];
}
}

app.controller(
'GisMapController',
($scope, $route, kbnUrl, localStorage, AppState, globalState) => {
Expand Down Expand Up @@ -333,7 +360,7 @@ app.controller(
store.dispatch(setOpenTOCDetails(_.get(uiState, 'openTOCDetails', [])));
}

const layerList = getInitialLayers(savedMap.layerListJSON);
const layerList = getInitialLayers(savedMap.layerListJSON, getInitialLayersFromUrlParam());
initialLayerListConfig = copyPersistentState(layerList);
store.dispatch(replaceLayerList(layerList));
store.dispatch(setRefreshConfig($scope.refreshConfig));
Expand Down
50 changes: 50 additions & 0 deletions x-pack/test/functional/apps/maps/discover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import expect from '@kbn/expect';

export default function({ getService, getPageObjects }) {
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'maps', 'timePicker']);

describe('discover visualize button', () => {
beforeEach(async () => {
await PageObjects.common.navigateToApp('discover');
});

it('should link geo_shape fields to Maps application', async () => {
await PageObjects.discover.selectIndexPattern('geo_shapes*');
await PageObjects.discover.clickFieldListItem('geometry');
await PageObjects.discover.clickFieldListItemVisualize('geometry');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
const doesLayerExist = await PageObjects.maps.doesLayerExist('geo_shapes*');
expect(doesLayerExist).to.equal(true);
const hits = await PageObjects.maps.getHits();
expect(hits).to.equal('4');
});

it('should link geo_point fields to Maps application with time and query context', async () => {
await PageObjects.discover.selectIndexPattern('logstash-*');
await PageObjects.timePicker.setAbsoluteRange(
'Sep 22, 2015 @ 00:00:00.000',
'Sep 22, 2015 @ 04:00:00.000'
);
await queryBar.setQuery('machine.os.raw : "ios"');
await queryBar.submitQuery();
await PageObjects.header.waitUntilLoadingHasFinished();

await PageObjects.discover.clickFieldListItem('geo.coordinates');
await PageObjects.discover.clickFieldListItemVisualize('geo.coordinates');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.maps.waitForLayersToLoad();
const doesLayerExist = await PageObjects.maps.doesLayerExist('logstash-*');
expect(doesLayerExist).to.equal(true);
const hits = await PageObjects.maps.getHits();
expect(hits).to.equal('7');
});
});
}
1 change: 1 addition & 0 deletions x-pack/test/functional/apps/maps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function({ loadTestFile, getService }) {
loadTestFile(require.resolve('./import_geojson'));
loadTestFile(require.resolve('./layer_errors'));
loadTestFile(require.resolve('./embeddable'));
loadTestFile(require.resolve('./discover'));
});
});
}

0 comments on commit a32ffd5

Please sign in to comment.