[Maps] Do not wait for meta call to complete for bootstrapping the app (#29514)

This commit is contained in:
Thomas Neirynck 2019-01-31 10:43:09 -05:00 committed by GitHub
parent cbefe90d50
commit 98a77ab9db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 453 additions and 199 deletions

View file

@ -9,3 +9,5 @@ export const GIS_API_PATH = 'api/gis';
export const DECIMAL_DEGREES_PRECISION = 5; // meters precision
export const ZOOM_PRECISION = 2;
export const DEFAULT_EMS_TILE_LAYER = 'road_map';

View file

@ -6,12 +6,25 @@
import _ from 'lodash';
import { KibanaTilemapSource } from '../shared/layers/sources/kibana_tilemap_source';
import { EMSTMSSource } from '../shared/layers/sources/ems_tms_source';
import { isMetaDataLoaded, getDataSourcesSync } from '../meta';
import { DEFAULT_EMS_TILE_LAYER } from '../../common/constants';
export function getInitialLayers(savedMapLayerListJSON) {
export function getInitialLayers(savedMapLayerListJSON, dataSources) {
if (savedMapLayerListJSON) {
return JSON.parse(savedMapLayerListJSON);
}
if (!isMetaDataLoaded()) {
const descriptor = EMSTMSSource.createDescriptor(DEFAULT_EMS_TILE_LAYER);
const source = new EMSTMSSource(descriptor);
const layer = source.createDefaultLayer();
return [
layer.toLayerDescriptor()
];
}
const dataSources = getDataSourcesSync();
const kibanaTilemapUrl = _.get(dataSources, 'kibana.tilemap.url');
if (kibanaTilemapUrl) {
const sourceDescriptor = KibanaTilemapSource.createDescriptor(kibanaTilemapUrl);
@ -32,7 +45,5 @@ export function getInitialLayers(savedMapLayerListJSON, dataSources) {
];
}
// TODO display (or throw) warning that no tile layers are available and map.tilemap needs to be configured
// because EMS is unreachable or has been turned off on purpose.
return [];
}

View file

@ -4,6 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../meta', () => {
return {};
});
import { getInitialLayers } from './get_initial_layers';
const mockKibanaDataSource = {
@ -28,22 +32,65 @@ describe('Saved object has layer list', () => {
}
];
const layerListJSON = JSON.stringify(layerListFromSavedObject);
const dataSources = {
kibana: mockKibanaDataSource,
ems: mockEmsDataSource
};
expect(getInitialLayers(layerListJSON, dataSources)).toEqual(layerListFromSavedObject);
expect((getInitialLayers(layerListJSON))).toEqual(layerListFromSavedObject);
});
});
describe('Saved object does not have layer list', () => {
beforeEach(() => {
require('../meta').isMetaDataLoaded = () => {
return true;
};
});
function mockDataSourceResponse(dataSources) {
require('../meta').getDataSourcesSync = () => {
return dataSources;
};
require('../meta').isMetaDataLoaded = () => {
return true;
};
}
it('should get the default EMS layer when metadata has not loaded yet', () => {
mockDataSourceResponse();
require('../meta').isMetaDataLoaded = () => {
return false;
};
const layers = getInitialLayers(null);
expect(layers).toEqual([{
"alpha": 1,
"dataRequests": [],
"id": layers[0].id,
"label": null,
"maxZoom": 24,
"minZoom": 0,
"sourceDescriptor": {
"type": "EMS_TMS",
"id": "road_map",
},
"style": {
"properties": {},
"type": "TILE",
},
"temporary": false,
"type": "TILE",
"visible": true,
}]);
});
it('Should get initial layer from Kibana tilemap data source when Kibana tilemap is configured ', () => {
const dataSources = {
mockDataSourceResponse({
kibana: mockKibanaDataSource,
ems: mockEmsDataSource
};
});
const layers = getInitialLayers(null, dataSources);
const layers = getInitialLayers(null);
expect(layers).toEqual([{
"alpha": 1,
dataRequests: [],
@ -70,8 +117,9 @@ describe('Saved object does not have layer list', () => {
const dataSources = {
ems: mockEmsDataSource
};
mockDataSourceResponse(dataSources);
const layers = getInitialLayers(null, dataSources);
const layers = getInitialLayers(null);
expect(layers).toEqual([{
"alpha": 1,
dataRequests: [],
@ -100,10 +148,12 @@ describe('Saved object does not have layer list', () => {
tms: []
}
};
expect(getInitialLayers(null, dataSources)).toEqual([]);
mockDataSourceResponse(dataSources);
expect((getInitialLayers(null))).toEqual([]);
});
it('Should return empty list when no dataSoures are provided', () => {
expect(getInitialLayers(null, null)).toEqual([]);
mockDataSourceResponse(null);
expect((getInitialLayers(null))).toEqual([]);
});
});

View file

@ -20,7 +20,7 @@ import {
setQuery,
} from '../actions/store_actions';
import { updateFlyout, FLYOUT_STATE } from '../store/ui';
import { getDataSources, getUniqueIndexPatternIds } from '../selectors/map_selectors';
import { getUniqueIndexPatternIds } from '../selectors/map_selectors';
import { Inspector } from 'ui/inspector';
import { inspectorAdapters, indexPatternService } from '../kibana_services';
import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal';
@ -139,7 +139,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
}));
}
const layerList = getInitialLayers(savedMap.layerListJSON, getDataSources(store.getState()));
const layerList = getInitialLayers(savedMap.layerListJSON);
store.dispatch(replaceLayerList(layerList));
store.dispatch(setRefreshConfig($scope.refreshConfig));

View file

@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import { AddLayerPanel } from './view';
import { getFlyoutDisplay, updateFlyout, FLYOUT_STATE }
from '../../store/ui';
import { getTemporaryLayers, getDataSources } from "../../selectors/map_selectors";
import { getTemporaryLayers } from "../../selectors/map_selectors";
import {
addLayer,
removeLayer,
@ -19,14 +19,12 @@ import _ from 'lodash';
function mapStateToProps(state = {}) {
const dataSourceMeta = getDataSources(state);
function isLoading() {
const tmp = getTemporaryLayers(state);
return tmp.some((layer) => layer.isLayerLoading());
}
return {
flyoutVisible: getFlyoutDisplay(state) !== FLYOUT_STATE.NONE,
dataSourcesMeta: dataSourceMeta,
layerLoading: isLoading(),
temporaryLayers: !_.isEmpty(getTemporaryLayers(state))
};

View file

@ -108,8 +108,7 @@ export class AddLayerPanel extends Component {
_renderSourceEditor() {
const editorProperties = {
onPreviewSource: this._previewLayer,
dataSourcesMeta: this.props.dataSourcesMeta
onPreviewSource: this._previewLayer
};
const Source = ALL_SOURCES.find((Source) => {

View file

@ -0,0 +1,60 @@
/*
* 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 { GIS_API_PATH } from '../common/constants';
import _ from 'lodash';
const GIS_API_RELATIVE = `../${GIS_API_PATH}`;
let meta = null;
let isLoaded = false;
export async function getDataSources() {
if (!meta) {
meta = new Promise(async (resolve, reject) => {
try {
const meta = await fetch(`${GIS_API_RELATIVE}/meta`);
const metaJson = await meta.json();
isLoaded = true;
resolve(metaJson.data_sources);
} catch(e) {
reject(e);
}
});
}
return meta;
}
export function getDataSourcesSync() {
if (!isLoaded) {
throw new Error('Metadata is not loaded yet. Use isMetadataLoaded first before calling this function.');
}
return meta;
}
export function isMetaDataLoaded() {
return isLoaded;
}
export async function getEmsVectorFilesMeta() {
const dataSource = await getDataSources();
return _.get(dataSource, 'ems.file', []);
}
export async function getEmsTMSServices() {
const dataSource = await getDataSources();
return _.get(dataSource, 'ems.tms', []);
}
export async function getKibanaRegionList() {
const dataSource = await getDataSources();
return _.get(dataSource, 'kibana.regionmap', []);
}
export async function getKibanaTileMap() {
const dataSource = await getDataSources();
return _.get(dataSource, 'kibana.tilemap', []);
}

View file

@ -15,8 +15,8 @@ import { HeatmapStyle } from '../shared/layers/styles/heatmap_style';
import { TileStyle } from '../shared/layers/styles/tile_style';
import { timefilter } from 'ui/timefilter';
function createLayerInstance(layerDescriptor, dataSources) {
const source = createSourceInstance(layerDescriptor.sourceDescriptor, dataSources);
function createLayerInstance(layerDescriptor) {
const source = createSourceInstance(layerDescriptor.sourceDescriptor);
const style = createStyleInstance(layerDescriptor.style);
switch (layerDescriptor.type) {
case TileLayer.type:
@ -30,21 +30,14 @@ function createLayerInstance(layerDescriptor, dataSources) {
}
}
function createSourceInstance(sourceDescriptor, dataSources) {
const dataMeta = {
emsTmsServices: _.get(dataSources, 'ems.tms', []),
emsFileLayers: _.get(dataSources, 'ems.file', []),
ymlFileLayers: _.get(dataSources, 'kibana.regionmap', [])
};
function createSourceInstance(sourceDescriptor) {
const Source = ALL_SOURCES.find(Source => {
return Source.type === sourceDescriptor.type;
});
if (!Source) {
throw new Error(`Unrecognized sourceType ${sourceDescriptor.type}`);
}
return new Source(sourceDescriptor, dataMeta);
return new Source(sourceDescriptor);
}
@ -66,8 +59,6 @@ function createStyleInstance(styleDescriptor) {
}
}
export const getMapState = ({ map }) => map && map.mapState;
export const getMapReady = ({ map }) => map && map.ready;
export const getGoto = ({ map }) => map && map.goto;
@ -137,14 +128,12 @@ export const getDataFilters = createSelector(
}
);
export const getDataSources = createSelector(getMetadata, metadata => metadata ? metadata.data_sources : null);
export const getLayerList = createSelector(
getLayerListRaw,
getDataSources,
(layerDescriptorList, dataSources) => {
(layerDescriptorList) => {
return layerDescriptorList.map(layerDescriptor =>
createLayerInstance(layerDescriptor, dataSources));
createLayerInstance(layerDescriptor));
});
export const getSelectedLayer = createSelector(

View file

@ -0,0 +1,64 @@
/*
* 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 React from 'react';
import {
EuiSelect,
EuiFormRow,
} from '@elastic/eui';
import { getEmsVectorFilesMeta } from '../../../../meta';
export class EMSFileCreateSourceEditor extends React.Component {
state = {
emsFileOptionsRaw: null
};
_loadFileOptions = async () => {
const options = await getEmsVectorFilesMeta();
if (this._isMounted) {
this.setState({
emsFileOptionsRaw: options
});
}
}
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this._loadFileOptions();
}
render() {
if (!this.state.emsFileOptionsRaw) {
return null;
}
const emsVectorOptions = this.state.emsFileOptionsRaw ? this.state.emsFileOptionsRaw.map((file) => ({
value: file.id,
text: file.name
})) : [];
return (
<EuiFormRow label="Layer">
<EuiSelect
hasNoInitialSelection
options={emsVectorOptions}
onChange={this.props.onChange}
/>
</EuiFormRow>
);
}
}

View file

@ -4,15 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractVectorSource } from './vector_source';
import { AbstractVectorSource } from '../vector_source';
import React from 'react';
import {
EuiSelect,
EuiFormRow,
} from '@elastic/eui';
import { GIS_API_PATH } from '../../../../common/constants';
import { emsServiceSettings } from '../../../kibana_services';
import { GIS_API_PATH } from '../../../../../common/constants';
import { emsServiceSettings } from '../../../../kibana_services';
import { getEmsVectorFilesMeta } from '../../../../meta';
import { EMSFileCreateSourceEditor } from './create_source_editor';
export class EMSFileSource extends AbstractVectorSource {
@ -28,38 +25,23 @@ export class EMSFileSource extends AbstractVectorSource {
};
}
static renderEditor({ dataSourcesMeta, onPreviewSource }) {
const emsVectorOptionsRaw = (dataSourcesMeta) ? dataSourcesMeta.ems.file : [];
const emsVectorOptions = emsVectorOptionsRaw ? emsVectorOptionsRaw.map((file) => ({
value: file.id,
text: file.name
})) : [];
static renderEditor({ onPreviewSource }) {
const onChange = ({ target }) => {
const selectedId = target.options[target.selectedIndex].value;
const emsFileSourceDescriptor = EMSFileSource.createDescriptor(selectedId);
const emsFileSource = new EMSFileSource(emsFileSourceDescriptor, emsVectorOptionsRaw);
const emsFileSource = new EMSFileSource(emsFileSourceDescriptor);
onPreviewSource(emsFileSource);
};
return (
<EuiFormRow label="Layer">
<EuiSelect
hasNoInitialSelection
options={emsVectorOptions}
onChange={onChange}
/>
</EuiFormRow>
);
return <EMSFileCreateSourceEditor onChange={onChange}/>;
}
constructor(descriptor, { emsFileLayers }) {
constructor(descriptor) {
super(descriptor);
this._emsFiles = emsFileLayers;
}
async getGeoJsonWithMeta() {
const fileSource = this._emsFiles.find((source => source.id === this._descriptor.id));
const emsFiles = await getEmsVectorFilesMeta();
const fileSource = emsFiles.find((source => source.id === this._descriptor.id));
const fetchUrl = `../${GIS_API_PATH}/data/ems?id=${encodeURIComponent(this._descriptor.id)}`;
const featureCollection = await AbstractVectorSource.getGeoJson(fileSource, fetchUrl);
return {
@ -77,24 +59,24 @@ export class EMSFileSource extends AbstractVectorSource {
}
async getDisplayName() {
const fileSource = this._emsFiles.find((source => source.id === this._descriptor.id));
const emsFiles = await getEmsVectorFilesMeta();
const fileSource = emsFiles.find((source => source.id === this._descriptor.id));
return fileSource.name;
}
async getAttributions() {
const fileSource = this._emsFiles.find((source => source.id === this._descriptor.id));
const emsFiles = await getEmsVectorFilesMeta();
const fileSource = emsFiles.find((source => source.id === this._descriptor.id));
return fileSource.attributions;
}
async getStringFields() {
//todo: use map/service-settings instead.
const fileSource = this._emsFiles.find((source => source.id === this._descriptor.id));
const emsFiles = await getEmsVectorFilesMeta();
const fileSource = emsFiles.find((source => source.id === this._descriptor.id));
return fileSource.fields.map(f => {
return { name: f.name, label: f.description };
});
}
canFormatFeatureProperties() {

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export { EMSFileSource } from './ems_file_source';

View file

@ -0,0 +1,64 @@
/*
* 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 React from 'react';
import {
EuiSelect,
EuiFormRow,
} from '@elastic/eui';
import { getEmsTMSServices } from '../../../../meta';
export class EMSTMSCreateSourceEditor extends React.Component {
state = {
emsTmsOptionsRaw: null
};
_loadTmsOptions = async () => {
const options = await getEmsTMSServices();
if (this._isMounted) {
this.setState({
emsTmsOptionsRaw: options
});
}
}
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this._loadTmsOptions();
}
render() {
if (!this.state.emsTmsOptionsRaw) {
return null;
}
const emsTileOptions = this.state.emsTmsOptionsRaw.map((service) => ({
value: service.id,
text: service.id //due to not having human readable names
}));
return (
<EuiFormRow label="Tile service">
<EuiSelect
hasNoInitialSelection
options={emsTileOptions}
onChange={this.props.onChange}
/>
</EuiFormRow>
);
}
}

View file

@ -6,11 +6,9 @@
import React from 'react';
import { AbstractTMSSource } from '../tms_source';
import { TileLayer } from '../../tile_layer';
import {
EuiSelect,
EuiFormRow,
} from '@elastic/eui';
import _ from 'lodash';
import { getEmsTMSServices } from '../../../../meta';
import { EMSTMSCreateSourceEditor } from './create_source_editor';
export class EMSTMSSource extends AbstractTMSSource {
@ -27,34 +25,20 @@ export class EMSTMSSource extends AbstractTMSSource {
};
}
static renderEditor({ dataSourcesMeta, onPreviewSource }) {
const emsTmsOptionsRaw = _.get(dataSourcesMeta, "ems.tms", []);
const emsTileOptions = emsTmsOptionsRaw.map((service) => ({
value: service.id,
text: service.id //due to not having human readable names
}));
static renderEditor({ onPreviewSource }) {
const onChange = ({ target }) => {
const selectedId = target.options[target.selectedIndex].value;
const emsTMSSourceDescriptor = EMSTMSSource.createDescriptor(selectedId);
const emsTMSSource = new EMSTMSSource(emsTMSSourceDescriptor, emsTmsOptionsRaw);
const emsTMSSource = new EMSTMSSource(emsTMSSourceDescriptor);
onPreviewSource(emsTMSSource);
};
return (
<EuiFormRow label="Tile service">
<EuiSelect
hasNoInitialSelection
options={emsTileOptions}
onChange={onChange}
/>
</EuiFormRow>
);
return <EMSTMSCreateSourceEditor onChange={onChange}/>;
}
constructor(descriptor, { emsTmsServices }) {
constructor(descriptor) {
super(descriptor);
this._emsTileServices = emsTmsServices;
}
async getImmutableProperties() {
@ -64,12 +48,13 @@ export class EMSTMSSource extends AbstractTMSSource {
];
}
_getTMSOptions() {
if(!this._emsTileServices) {
async _getTMSOptions() {
const emsTileServices = await getEmsTMSServices();
if(!emsTileServices) {
return;
}
return this._emsTileServices.find(service => {
return emsTileServices.find(service => {
return service.id === this._descriptor.id;
});
}
@ -93,7 +78,7 @@ export class EMSTMSSource extends AbstractTMSSource {
}
async getAttributions() {
const service = this._getTMSOptions();
const service = await this._getTMSOptions();
if (!service || !service.attributionMarkdown) {
return [];
}
@ -110,10 +95,10 @@ export class EMSTMSSource extends AbstractTMSSource {
});
}
getUrlTemplate() {
const service = this._getTMSOptions();
async getUrlTemplate() {
const service = await this._getTMSOptions();
if (!service || !service.url) {
throw new Error('Can not generate EMS TMS url template');
throw new Error('Cannot generate EMS TMS url template');
}
return service.url;
}

View file

@ -4,6 +4,21 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../../../../meta', () => {
return {
getEmsTMSServices: async () => {
return [
{
id: 'road_map',
attributionMarkdown: '[foobar](http://foobar.org) | [foobaz](http://foobaz.org)'
}, {
id: 'satellite',
attributionMarkdown: '[satellite](http://satellite.org)'
}
];
}
};
});
import {
EMSTMSSource,
@ -15,21 +30,10 @@ describe('EMSTMSSource', () => {
const emsTmsSource = new EMSTMSSource({
id: 'road_map'
}, {
emsTmsServices: [
{
id: 'road_map',
attributionMarkdown: '[foobar](http://foobar.org) | [foobaz](http://foobaz.org)'
}, {
id: 'satellite',
attributionMarkdown: '[satellite](http://satellite.org)'
}
]
});
const attributions = await emsTmsSource.getAttributions();
expect(attributions).toEqual([
{
label: 'foobar',

View file

@ -10,48 +10,70 @@ import {
EuiSelect,
EuiFormRow,
} from '@elastic/eui';
import { getKibanaRegionList } from '../../../../meta';
const NO_REGIONMAP_LAYERS_MSG =
'No vector layers are available.' +
' Ask your system administrator to set "map.regionmap" in kibana.yml.';
export function CreateSourceEditor({ onSelect, regionmapLayers }) {
export class CreateSourceEditor extends React.Component {
const regionmapOptions = regionmapLayers.map(({ name, url }) => {
return {
value: url,
text: name
};
});
state = {
regionmapLayers: []
}
const onChange = ({ target }) => {
const selectedName = target.options[target.selectedIndex].text;
onSelect({ name: selectedName });
_loadList = async () => {
const list = await getKibanaRegionList();
if (this._isMounted) {
this.setState({
regionmapLayers: list
});
}
};
return (
<EuiFormRow
label="Vector layer"
helpText={regionmapLayers.length === 0 ? NO_REGIONMAP_LAYERS_MSG : null}
>
<EuiSelect
hasNoInitialSelection
options={regionmapOptions}
onChange={onChange}
disabled={regionmapLayers.length === 0}
/>
</EuiFormRow>
);
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this._loadList();
}
render() {
const onChange = ({ target }) => {
const selectedName = target.options[target.selectedIndex].text;
this.props.onSelect({ name: selectedName });
};
const regionmapOptions = this.state.regionmapLayers.map(({ name, url }) => {
return {
value: url,
text: name
};
});
return (
<EuiFormRow
label="Vector layer"
helpText={this.state.regionmapLayers.length === 0 ? NO_REGIONMAP_LAYERS_MSG : null}
>
<EuiSelect
hasNoInitialSelection
options={regionmapOptions}
onChange={onChange}
disabled={this.state.regionmapLayers.length === 0}
/>
</EuiFormRow>
);
}
}
CreateSourceEditor.propTypes = {
onSelect: PropTypes.func.isRequired,
regionmapLayers: PropTypes.arrayOf(PropTypes.shape({
url: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})),
onSelect: PropTypes.func.isRequired
};
CreateSourceEditor.defaultProps = {
regionmapLayers: [],
};

View file

@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import _ from 'lodash';
import { AbstractVectorSource } from '../vector_source';
import React from 'react';
import { CreateSourceEditor } from './create_source_editor';
import { getKibanaRegionList } from '../../../../meta';
export class KibanaRegionmapSource extends AbstractVectorSource {
static type = 'REGIONMAP_FILE';
@ -16,9 +17,8 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
static description = 'Vector shapes from static files configured in kibana.yml';
static icon = 'logoKibana';
constructor(descriptor, { ymlFileLayers }) {
constructor(descriptor) {
super(descriptor);
this._regionList = ymlFileLayers;
}
static createDescriptor(options) {
@ -28,19 +28,16 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
};
}
static renderEditor = ({ dataSourcesMeta, onPreviewSource }) => {
const regionmapLayers = _.get(dataSourcesMeta, 'kibana.regionmap', []);
static renderEditor = ({ onPreviewSource }) => {
const onSelect = (layerConfig) => {
const sourceDescriptor = KibanaRegionmapSource.createDescriptor(layerConfig);
const source = new KibanaRegionmapSource(sourceDescriptor, { ymlFileLayers: regionmapLayers });
const source = new KibanaRegionmapSource(sourceDescriptor);
onPreviewSource(source);
};
return (
<CreateSourceEditor
onSelect={onSelect}
regionmapLayers={regionmapLayers}
/>
);
};
@ -53,7 +50,8 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
}
async getGeoJsonWithMeta() {
const fileSource = this._regionList.find(source => source.name === this._descriptor.name);
const regionList = await getKibanaRegionList();
const fileSource = regionList.find(source => source.name === this._descriptor.name);
const featureCollection = await AbstractVectorSource.getGeoJson(fileSource, fileSource.url);
return {
data: featureCollection
@ -61,8 +59,8 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
}
async getStringFields() {
//todo: use map/service-settings instead.
const fileSource = this._regionList.find((source => source.name === this._descriptor.name));
const regionList = await getKibanaRegionList();
const fileSource = regionList.find((source => source.name === this._descriptor.name));
return fileSource.fields.map(f => {
return { name: f.name, label: f.description };

View file

@ -11,27 +11,54 @@ import {
EuiFormRow,
} from '@elastic/eui';
import { getKibanaTileMap } from '../../../../meta';
const NO_TILEMAP_LAYER_MSG =
'No tilemap layer is available.' +
' Ask your system administrator to set "map.tilemap.url" in kibana.yml.';
export class CreateSourceEditor extends Component {
componentDidMount() {
if (this.props.url) {
state = {
url: null
}
_loadUrl = async () => {
const tilemap = await getKibanaTileMap();
if (this._isMounted) {
this.setState({
url: tilemap.url
});
this.props.previewTilemap(this.props.url);
}
}
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this._loadUrl();
}
render() {
if (this.state.url === null) {
return null;
}
return (
<EuiFormRow
label="Tilemap url"
helpText={this.props.url ? null : NO_TILEMAP_LAYER_MSG}
helpText={this.state.url ? null : NO_TILEMAP_LAYER_MSG}
>
<EuiFieldText
readOnly
value={this.props.url}
value={this.state.url}
/>
</EuiFormRow>
);
@ -39,6 +66,5 @@ export class CreateSourceEditor extends Component {
}
CreateSourceEditor.propTypes = {
previewTilemap: PropTypes.func.isRequired,
url: PropTypes.string,
previewTilemap: PropTypes.func.isRequired
};

View file

@ -7,7 +7,6 @@ import React from 'react';
import { AbstractTMSSource } from '../tms_source';
import { TileLayer } from '../../tile_layer';
import { CreateSourceEditor } from './create_source_editor';
export class KibanaTilemapSource extends AbstractTMSSource {
static type = 'KIBANA_TILEMAP';
@ -22,20 +21,19 @@ export class KibanaTilemapSource extends AbstractTMSSource {
};
}
static renderEditor = ({ dataSourcesMeta, onPreviewSource }) => {
const { url } = dataSourcesMeta ? dataSourcesMeta.kibana.tilemap : {};
static renderEditor = ({ onPreviewSource }) => {
const previewTilemap = (urlTemplate) => {
const sourceDescriptor = KibanaTilemapSource.createDescriptor(urlTemplate);
const source = new KibanaTilemapSource(sourceDescriptor);
onPreviewSource(source);
};
return (<CreateSourceEditor previewTilemap={previewTilemap} url={url} />);
return (<CreateSourceEditor previewTilemap={previewTilemap} />);
};
async getImmutableProperties() {
return [
{ label: 'Data source', value: KibanaTilemapSource.title },
{ label: 'Tilemap url', value: this.getUrlTemplate() },
{ label: 'Tilemap url', value: (await this.getUrlTemplate()) },
];
}
@ -54,11 +52,11 @@ export class KibanaTilemapSource extends AbstractTMSSource {
}
getUrlTemplate() {
async getUrlTemplate() {
return this._descriptor.url;
}
async getDisplayName() {
return this.getUrlTemplate();
return await this.getUrlTemplate();
}
}

View file

@ -8,7 +8,7 @@
import { AbstractSource } from './source';
export class AbstractTMSSource extends AbstractSource {
getUrlTemplate() {
async getUrlTemplate() {
throw new Error('Should implement TMSSource#getUrlTemplate');
}
}

View file

@ -64,7 +64,23 @@ export class TileLayer extends AbstractLayer {
});
}
async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) {
if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) {
return;
}
const sourceDataId = 'source';
const requestToken = Symbol(`layer-source-refresh:${ this.getId()} - source`);
startLoading(sourceDataId, requestToken, dataFilters);
try {
const url = await this._source.getUrlTemplate();
stopLoading(sourceDataId, requestToken, url, {});
} catch(error) {
onLoadError(sourceDataId, requestToken, error.message);
}
}
async syncLayerWithMB(mbMap) {
const source = mbMap.getSource(this.getId());
const mbLayerId = this.getId() + '_raster';
@ -74,7 +90,12 @@ export class TileLayer extends AbstractLayer {
return;
}
const url = this._source.getUrlTemplate();
const sourceDataRquest = this.getSourceDataRequest();
const url = sourceDataRquest.getData();
if (!url) {
return;
}
const sourceId = this.getId();
mbMap.addSource(sourceId, {
type: 'raster',

View file

@ -1,22 +0,0 @@
/*
* 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 { SET_META } from '../actions/store_actions';
const INITIAL_STATE = {
meta: null
};
// Reducer
function config(state = INITIAL_STATE, action) {
switch (action.type) {
case SET_META:
return { ...state, meta: action.meta };
default:
return state;
}
}
export default config;

View file

@ -8,16 +8,13 @@ import { combineReducers, applyMiddleware, createStore, compose } from 'redux';
import thunk from 'redux-thunk';
import ui from './ui';
import { map } from './map';
import { loadMetaResources } from "../actions/store_actions";
import config from './config';
// TODO this should not be exported and all access to the store be via getStore
export let store;
const rootReducer = combineReducers({
map,
ui,
config
ui
});
const enhancers = [ applyMiddleware(thunk) ];
@ -36,6 +33,5 @@ export const getStore = async function () {
storeConfig,
composeEnhancers(...enhancers)
);
await loadMetaResources(store.dispatch);
return store;
};