[maps] fix space in geo field name cause vector tiles to break (#148413)

Fixes https://github.com/elastic/kibana/issues/131468

URL encode geo field name when generating _mvt URL.

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2023-01-04 19:52:15 -05:00 committed by GitHub
parent 2f973afd1c
commit 872cdbbee9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 120 additions and 66 deletions

View file

@ -5,73 +5,123 @@
* 2.0.
*/
import { decodeMvtResponseBody, encodeMvtResponseBody } from './mvt_request_body';
import {
decodeMvtResponseBody,
encodeMvtResponseBody,
getAggsTileRequest,
getHitsTileRequest,
} from './mvt_request_body';
import { RENDER_AS } from './constants';
test('Should encode shape into URI safe string and decode back to original shape', () => {
const searchRequest = {
docvalue_fields: [],
size: 10000,
_source: false,
script_fields: {},
stored_fields: ['geopoint'],
runtime_mappings: {
'day of week': {
type: 'keyword',
script: {
source:
"ZonedDateTime input = doc['ISSUE_DATE'].value;\nString output = input.format(DateTimeFormatter.ofPattern('e')) + ' ' + input.format(DateTimeFormatter.ofPattern('E'));\nemit(output);",
describe('decodeMvtResponseBody', () => {
test('Should encode shape into URI safe string and decode back to original shape', () => {
const searchRequest = {
docvalue_fields: [],
size: 10000,
_source: false,
script_fields: {},
stored_fields: ['geopoint'],
runtime_mappings: {
'day of week': {
type: 'keyword',
script: {
source:
"ZonedDateTime input = doc['ISSUE_DATE'].value;\nString output = input.format(DateTimeFormatter.ofPattern('e')) + ' ' + input.format(DateTimeFormatter.ofPattern('E'));\nemit(output);",
},
},
},
},
query: {
bool: {
must: [],
filter: [],
should: [],
must_not: [],
},
},
};
const encodedSearchRequest = encodeMvtResponseBody(searchRequest);
expect(encodedSearchRequest).toBe(
`(_source%3A!f%2Cdocvalue_fields%3A!()%2Cquery%3A(bool%3A(filter%3A!()%2Cmust%3A!()%2Cmust_not%3A!()%2Cshould%3A!()))%2Cruntime_mappings%3A('day%20of%20week'%3A(script%3A(source%3A'ZonedDateTime%20input%20%3D%20doc%5B!'ISSUE_DATE!'%5D.value%3B%0AString%20output%20%3D%20input.format(DateTimeFormatter.ofPattern(!'e!'))%20%2B%20!'%20!'%20%2B%20input.format(DateTimeFormatter.ofPattern(!'E!'))%3B%0Aemit(output)%3B')%2Ctype%3Akeyword))%2Cscript_fields%3A()%2Csize%3A10000%2Cstored_fields%3A!(geopoint))`
);
expect(decodeMvtResponseBody(encodedSearchRequest)).toEqual(searchRequest);
});
test(`Should handle '%' character`, () => {
const runtimeFieldScript = `if (doc['price'].size() != 0){
String tmp=dissect('$%{price}').extract(doc["price"].value)?.price;
tmp = tmp.replace(',','');
def pn = Double.parseDouble( tmp );
if (pn != null) emit(pn);
}
else {
emit(0)
}`;
const searchRequest = {
size: 10000,
_source: false,
runtime_mappings: {
price_as_number: {
type: 'keyword',
script: {
source: runtimeFieldScript,
query: {
bool: {
must: [],
filter: [],
should: [],
must_not: [],
},
},
},
query: {
bool: {
must: [],
filter: [],
should: [],
must_not: [],
};
const encodedSearchRequest = encodeMvtResponseBody(searchRequest);
expect(encodedSearchRequest).toBe(
`(_source%3A!f%2Cdocvalue_fields%3A!()%2Cquery%3A(bool%3A(filter%3A!()%2Cmust%3A!()%2Cmust_not%3A!()%2Cshould%3A!()))%2Cruntime_mappings%3A('day%20of%20week'%3A(script%3A(source%3A'ZonedDateTime%20input%20%3D%20doc%5B!'ISSUE_DATE!'%5D.value%3B%0AString%20output%20%3D%20input.format(DateTimeFormatter.ofPattern(!'e!'))%20%2B%20!'%20!'%20%2B%20input.format(DateTimeFormatter.ofPattern(!'E!'))%3B%0Aemit(output)%3B')%2Ctype%3Akeyword))%2Cscript_fields%3A()%2Csize%3A10000%2Cstored_fields%3A!(geopoint))`
);
expect(decodeMvtResponseBody(encodedSearchRequest)).toEqual(searchRequest);
});
test(`Should handle '%' character`, () => {
const runtimeFieldScript = `if (doc['price'].size() != 0){
String tmp=dissect('$%{price}').extract(doc["price"].value)?.price;
tmp = tmp.replace(',','');
def pn = Double.parseDouble( tmp );
if (pn != null) emit(pn);
}
else {
emit(0)
}`;
const searchRequest = {
size: 10000,
_source: false,
runtime_mappings: {
price_as_number: {
type: 'keyword',
script: {
source: runtimeFieldScript,
},
},
},
},
};
const encodedSearchRequest = encodeMvtResponseBody(searchRequest);
expect(decodeMvtResponseBody(encodedSearchRequest)).toEqual(searchRequest);
query: {
bool: {
must: [],
filter: [],
should: [],
must_not: [],
},
},
};
const encodedSearchRequest = encodeMvtResponseBody(searchRequest);
expect(decodeMvtResponseBody(encodedSearchRequest)).toEqual(searchRequest);
});
});
describe('getAggsTileRequest', () => {
test(`Should URL encode path parameters`, () => {
const searchRequest = {
aggs: {},
runtime_mappings: {},
query: {},
};
const { path } = getAggsTileRequest({
encodedRequestBody: encodeMvtResponseBody(searchRequest),
geometryFieldName: 'my location',
gridPrecision: 8,
hasLabels: true,
index: 'my index',
renderAs: RENDER_AS.POINT,
x: 0,
y: 0,
z: 0,
});
expect(path).toEqual('/my%20index/_mvt/my%20location/0/0/0');
});
});
describe('getHitsTileRequest', () => {
test(`Should URL encode path parameters`, () => {
const searchRequest = {
size: 10000,
runtime_mappings: {},
query: {},
};
const { path } = getHitsTileRequest({
encodedRequestBody: encodeMvtResponseBody(searchRequest),
geometryFieldName: 'my location',
hasLabels: true,
index: 'my index',
x: 0,
y: 0,
z: 0,
});
expect(path).toEqual('/my%20index/_mvt/my%20location/0/0/0');
});
});

View file

@ -43,7 +43,9 @@ export function getAggsTileRequest({
}) {
const requestBody = decodeMvtResponseBody(encodedRequestBody) as any;
return {
path: `/${encodeURIComponent(index)}/_mvt/${geometryFieldName}/${z}/${x}/${y}`,
path: `/${encodeURIComponent(index)}/_mvt/${encodeURIComponent(
geometryFieldName
)}/${z}/${x}/${y}`,
body: {
size: 0, // no hits
grid_precision: gridPrecision,
@ -53,7 +55,7 @@ export function getAggsTileRequest({
grid_agg: renderAs === RENDER_AS.HEX ? 'geohex' : 'geotile',
grid_type: renderAs === RENDER_AS.GRID || renderAs === RENDER_AS.HEX ? 'grid' : 'centroid',
aggs: requestBody.aggs,
fields: requestBody.fields,
fields: requestBody.fields ? requestBody.fields : [],
runtime_mappings: requestBody.runtime_mappings,
with_labels: hasLabels,
},
@ -79,7 +81,9 @@ export function getHitsTileRequest({
}) {
const requestBody = decodeMvtResponseBody(encodedRequestBody) as any;
return {
path: `/${encodeURIComponent(index)}/_mvt/${geometryFieldName}/${z}/${x}/${y}`,
path: `/${encodeURIComponent(index)}/_mvt/${encodeURIComponent(
geometryFieldName
)}/${z}/${x}/${y}`,
body: {
grid_precision: 0, // no aggs
exact_bounds: true,