mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Maps] fix Kibana maps should not override the sort field if not provided by the user (#150400)
Fixes https://github.com/elastic/kibana/issues/150184 PR updates vector tile search request body generation to only populate `sort` property when its provided. PR also cleans up some `any` types with types from elasticsearch client.
This commit is contained in:
parent
b96d46c06b
commit
0958c59f0b
3 changed files with 84 additions and 22 deletions
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import {
|
||||
decodeMvtResponseBody,
|
||||
encodeMvtResponseBody,
|
||||
|
@ -23,7 +24,7 @@ describe('decodeMvtResponseBody', () => {
|
|||
stored_fields: ['geopoint'],
|
||||
runtime_mappings: {
|
||||
'day of week': {
|
||||
type: 'keyword',
|
||||
type: 'keyword' as estypes.MappingRuntimeFieldType,
|
||||
script: {
|
||||
source:
|
||||
"ZonedDateTime input = doc['ISSUE_DATE'].value;\nString output = input.format(DateTimeFormatter.ofPattern('e')) + ' ' + input.format(DateTimeFormatter.ofPattern('E'));\nemit(output);",
|
||||
|
@ -64,7 +65,7 @@ describe('decodeMvtResponseBody', () => {
|
|||
_source: false,
|
||||
runtime_mappings: {
|
||||
price_as_number: {
|
||||
type: 'keyword',
|
||||
type: 'keyword' as estypes.MappingRuntimeFieldType,
|
||||
script: {
|
||||
source: runtimeFieldScript,
|
||||
},
|
||||
|
@ -124,4 +125,43 @@ describe('getHitsTileRequest', () => {
|
|||
});
|
||||
expect(path).toEqual('/my%20index/_mvt/my%20location/0/0/0');
|
||||
});
|
||||
|
||||
describe('sort', () => {
|
||||
test(`Should include sort`, () => {
|
||||
const searchRequest = {
|
||||
size: 10000,
|
||||
runtime_mappings: {},
|
||||
query: {},
|
||||
sort: ['timestamp'],
|
||||
};
|
||||
const { body } = getHitsTileRequest({
|
||||
encodedRequestBody: encodeMvtResponseBody(searchRequest),
|
||||
geometryFieldName: 'my location',
|
||||
hasLabels: true,
|
||||
index: 'my index',
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
});
|
||||
expect(body).toHaveProperty('sort');
|
||||
});
|
||||
|
||||
test(`Should not include sort when sort not provided`, () => {
|
||||
const searchRequest = {
|
||||
size: 10000,
|
||||
runtime_mappings: {},
|
||||
query: {},
|
||||
};
|
||||
const { body } = getHitsTileRequest({
|
||||
encodedRequestBody: encodeMvtResponseBody(searchRequest),
|
||||
geometryFieldName: 'my location',
|
||||
hasLabels: true,
|
||||
index: 'my index',
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
});
|
||||
expect(body).not.toHaveProperty('sort');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,13 +6,16 @@
|
|||
*/
|
||||
|
||||
import rison from '@kbn/rison';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { RENDER_AS } from './constants';
|
||||
|
||||
export function decodeMvtResponseBody(encodedRequestBody: string): object {
|
||||
return rison.decode(decodeURIComponent(encodedRequestBody).replace('%25', '%')) as object;
|
||||
export function decodeMvtResponseBody(encodedRequestBody: string): estypes.SearchRequest['body'] {
|
||||
return rison.decode(
|
||||
decodeURIComponent(encodedRequestBody).replace('%25', '%')
|
||||
) as estypes.SearchRequest['body'];
|
||||
}
|
||||
|
||||
export function encodeMvtResponseBody(unencodedRequestBody: object): string {
|
||||
export function encodeMvtResponseBody(unencodedRequestBody: estypes.SearchRequest['body']): string {
|
||||
// URL encoding replaces unsafe ASCII characters with a '%' followed by two hexadecimal digits
|
||||
// encodeURIComponent does not encode '%'
|
||||
// This causes preexisting '%' to break decoding because they are not valid URL encoding
|
||||
|
@ -41,7 +44,10 @@ export function getAggsTileRequest({
|
|||
y: number;
|
||||
z: number;
|
||||
}) {
|
||||
const requestBody = decodeMvtResponseBody(encodedRequestBody) as any;
|
||||
const requestBody = decodeMvtResponseBody(encodedRequestBody);
|
||||
if (!requestBody) {
|
||||
throw new Error('Required requestBody parameter not provided');
|
||||
}
|
||||
return {
|
||||
path: `/${encodeURIComponent(index)}/_mvt/${encodeURIComponent(
|
||||
geometryFieldName
|
||||
|
@ -58,7 +64,7 @@ export function getAggsTileRequest({
|
|||
fields: requestBody.fields ? requestBody.fields : [],
|
||||
runtime_mappings: requestBody.runtime_mappings,
|
||||
with_labels: hasLabels,
|
||||
},
|
||||
} as estypes.SearchMvtRequest['body'],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -79,21 +85,30 @@ export function getHitsTileRequest({
|
|||
y: number;
|
||||
z: number;
|
||||
}) {
|
||||
const requestBody = decodeMvtResponseBody(encodedRequestBody) as any;
|
||||
const requestBody = decodeMvtResponseBody(encodedRequestBody);
|
||||
if (!requestBody) {
|
||||
throw new Error('Required requestBody parameter not provided');
|
||||
}
|
||||
const tileRequestBody = {
|
||||
grid_precision: 0, // no aggs
|
||||
exact_bounds: true,
|
||||
extent: 4096, // full resolution,
|
||||
query: requestBody.query,
|
||||
runtime_mappings: requestBody.runtime_mappings,
|
||||
track_total_hits: typeof requestBody.size === 'number' ? requestBody.size + 1 : false,
|
||||
with_labels: hasLabels,
|
||||
} as estypes.SearchMvtRequest['body'];
|
||||
if (requestBody.fields) {
|
||||
// @ts-expect-error SearchRequest['body'].fields and SearchMvtRequest['body'].fields types do not allign, even though they do in implemenation
|
||||
tileRequestBody.fields = requestBody.fields;
|
||||
}
|
||||
if (requestBody.sort) {
|
||||
tileRequestBody!.sort = requestBody.sort;
|
||||
}
|
||||
return {
|
||||
path: `/${encodeURIComponent(index)}/_mvt/${encodeURIComponent(
|
||||
geometryFieldName
|
||||
)}/${z}/${x}/${y}`,
|
||||
body: {
|
||||
grid_precision: 0, // no aggs
|
||||
exact_bounds: true,
|
||||
extent: 4096, // full resolution,
|
||||
query: requestBody.query,
|
||||
fields: requestBody.fields ? requestBody.fields : [],
|
||||
runtime_mappings: requestBody.runtime_mappings,
|
||||
sort: requestBody.sort ? requestBody.sort : [],
|
||||
track_total_hits: typeof requestBody.size === 'number' ? requestBody.size + 1 : false,
|
||||
with_labels: hasLabels,
|
||||
},
|
||||
body: tileRequestBody,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { CoreStart, KibanaRequest, KibanaResponseFactory, Logger } from '@kbn/co
|
|||
import { IRouter } from '@kbn/core/server';
|
||||
import type { DataRequestHandlerContext } from '@kbn/data-plugin/server';
|
||||
import { errors } from '@elastic/elasticsearch';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import {
|
||||
MVT_GETTILE_API_PATH,
|
||||
API_ROOT_PATH,
|
||||
|
@ -61,7 +62,10 @@ export function initMVTRoutes({
|
|||
const y = parseInt((params as any).y, 10) as number;
|
||||
const z = parseInt((params as any).z, 10) as number;
|
||||
|
||||
let tileRequest: { path: string; body: object } | undefined;
|
||||
let tileRequest: { path: string; body: estypes.SearchMvtRequest['body'] } = {
|
||||
path: '',
|
||||
body: {},
|
||||
};
|
||||
try {
|
||||
tileRequest = getHitsTileRequest({
|
||||
encodedRequestBody: query.requestBody as string,
|
||||
|
@ -123,7 +127,10 @@ export function initMVTRoutes({
|
|||
const y = parseInt((params as any).y, 10) as number;
|
||||
const z = parseInt((params as any).z, 10) as number;
|
||||
|
||||
let tileRequest: { path: string; body: object } | undefined;
|
||||
let tileRequest: { path: string; body: estypes.SearchMvtRequest['body'] } = {
|
||||
path: '',
|
||||
body: {},
|
||||
};
|
||||
try {
|
||||
tileRequest = getAggsTileRequest({
|
||||
encodedRequestBody: query.requestBody as string,
|
||||
|
@ -168,7 +175,7 @@ async function getTile({
|
|||
path,
|
||||
}: {
|
||||
abortController: AbortController;
|
||||
body: object;
|
||||
body: estypes.SearchMvtRequest['body'];
|
||||
context: DataRequestHandlerContext;
|
||||
core: CoreStart;
|
||||
executionContext: KibanaExecutionContext;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue