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

[SAVED_OBJECTS] [AGGS] Step I to add aggregations in the find of saved objects #64002

Closed
wants to merge 38 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b699c70
step 1 to add aggs in the find function of saved object
XavierM Apr 14, 2020
3819cd5
setp 2 - add specific unit test to aggs + fix bug found during integr…
XavierM Apr 15, 2020
1c4bac9
step 3 - add security api_integration arounds aggs
XavierM Apr 20, 2020
6feca9b
fix types
XavierM Apr 20, 2020
8dda821
unit test added for aggs_utils
XavierM Apr 20, 2020
0904a0e
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Apr 20, 2020
44dcb25
add documentation
XavierM Apr 22, 2020
560b63b
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Apr 22, 2020
699d0a1
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Apr 27, 2020
0f57677
fix docs
XavierM Apr 27, 2020
d7cb38e
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Apr 27, 2020
ec2a69d
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Jul 6, 2020
817a3ef
review I
XavierM Jul 7, 2020
f7faa18
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Jul 7, 2020
a54f440
doc
XavierM Jul 7, 2020
2aef7fd
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Jul 7, 2020
e698b0e
try to fix test
XavierM Jul 7, 2020
09565d8
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Jul 7, 2020
c16e72b
add the new property to the saved object globaltype
XavierM Jul 7, 2020
5a12d44
fix types
XavierM Jul 7, 2020
c4eff53
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Jul 7, 2020
1eaafca
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Nov 9, 2020
f04ab64
delete old files
XavierM Nov 9, 2020
8f64144
fix types + test api integration
XavierM Nov 9, 2020
2209d12
type fix + test
XavierM Nov 10, 2020
4f667d4
Update src/core/server/saved_objects/types.ts
XavierM Nov 11, 2020
433a71c
review I
XavierM Nov 11, 2020
37ad64d
Merge branch 'saved-objects-aggs' of github.com:XavierM/kibana into s…
XavierM Nov 11, 2020
2fb9a2c
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Nov 11, 2020
48dcdda
Merge branch 'master' into saved-objects-aggs
kibanamachine Nov 11, 2020
2dc3ff8
Merge branch 'master' into saved-objects-aggs
kibanamachine Nov 12, 2020
f1f437b
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Nov 20, 2020
b23eac7
change our validation to match discussion with Pierre and Rudolph
XavierM Nov 23, 2020
462d87e
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Nov 23, 2020
4e319b3
Validate multiple items nested filter query through KueryNode
XavierM Nov 23, 2020
ba55542
Merge branch 'master' of github.com:elastic/kibana into saved-objects…
XavierM Nov 23, 2020
16de875
remove unused import
XavierM Nov 23, 2020
cb7ada3
review + put back test
XavierM Nov 23, 2020
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
4 changes: 4 additions & 0 deletions docs/api/saved-objects/find.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ experimental[] Retrieve a paginated set of {kib} saved objects by various condit
It should look like that savedObjectType.attributes.title: "myTitle". However, If you used a direct attribute of a saved object like `updatedAt`,
you will have to define your filter like that savedObjectType.updatedAt > 2018-12-22.

`aggs`::
(Optional, string) The aggs will support aggregation string with the caveat that your field from the aggregation will have the attribute from your type saved object,
it should look like this: savedObjectType.attributes.field. However, If you use a direct attribute of a saved object like updatedAt, you will have to define your filter like this: savedObjectType.updatedAt.
XavierM marked this conversation as resolved.
Show resolved Hide resolved

NOTE: As objects change in {kib}, the results on each page of the response also
change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Find all SavedObjects matching the search query
<b>Signature:</b>

```typescript
find<T = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
find<T = unknown, A = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T, A>>;
```

## Parameters
Expand All @@ -20,5 +20,5 @@ find<T = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindRes

<b>Returns:</b>

`Promise<SavedObjectsFindResponse<T>>`
`Promise<SavedObjectsFindResponse<T, A>>`

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsFindOptions](./kibana-plugin-core-server.savedobjectsfindoptions.md) &gt; [aggs](./kibana-plugin-core-server.savedobjectsfindoptions.aggs.md)

## SavedObjectsFindOptions.aggs property

<b>Signature:</b>

```typescript
aggs?: string;
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions

| Property | Type | Description |
| --- | --- | --- |
| [aggs](./kibana-plugin-core-server.savedobjectsfindoptions.aggs.md) | <code>string</code> | |
| [defaultSearchOperator](./kibana-plugin-core-server.savedobjectsfindoptions.defaultsearchoperator.md) | <code>'AND' &#124; 'OR'</code> | |
| [fields](./kibana-plugin-core-server.savedobjectsfindoptions.fields.md) | <code>string[]</code> | An array of fields to include in the results |
| [filter](./kibana-plugin-core-server.savedobjectsfindoptions.filter.md) | <code>string</code> | |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsFindResponse](./kibana-plugin-core-server.savedobjectsfindresponse.md) &gt; [aggregations](./kibana-plugin-core-server.savedobjectsfindresponse.aggregations.md)

## SavedObjectsFindResponse.aggregations property

<b>Signature:</b>

```typescript
aggregations?: A;
```
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ Return type of the Saved Objects `find()` method.
<b>Signature:</b>

```typescript
export interface SavedObjectsFindResponse<T = unknown>
export interface SavedObjectsFindResponse<T = unknown, A = unknown>
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [aggregations](./kibana-plugin-core-server.savedobjectsfindresponse.aggregations.md) | <code>A</code> | |
| [page](./kibana-plugin-core-server.savedobjectsfindresponse.page.md) | <code>number</code> | |
| [per\_page](./kibana-plugin-core-server.savedobjectsfindresponse.per_page.md) | <code>number</code> | |
| [saved\_objects](./kibana-plugin-core-server.savedobjectsfindresponse.saved_objects.md) | <code>Array&lt;SavedObject&lt;T&gt;&gt;</code> | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
<b>Signature:</b>

```typescript
find<T = unknown>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
find<T = unknown, A = unknown>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, aggs, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T, A>>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| { search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, } | <code>SavedObjectsFindOptions</code> | |
| { search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, aggs, } | <code>SavedObjectsFindOptions</code> | |

<b>Returns:</b>

`Promise<SavedObjectsFindResponse<T>>`
`Promise<SavedObjectsFindResponse<T, A>>`

{<!-- -->promise<!-- -->} - { saved\_objects: \[{ id, type, version, attributes }<!-- -->\], total, per\_page, page }

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export declare class SavedObjectsRepository
| [delete(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.delete.md) | | Deletes an object |
| [deleteByNamespace(namespace, options)](./kibana-plugin-core-server.savedobjectsrepository.deletebynamespace.md) | | Deletes all objects from the provided namespace. |
| [deleteFromNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md) | | Removes one or more namespaces from a given multi-namespace saved object. If no namespaces remain, the saved object is deleted entirely. This method and \[<code>addToNamespaces</code>\][SavedObjectsRepository.addToNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to. |
| [find({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, })](./kibana-plugin-core-server.savedobjectsrepository.find.md) | | |
| [find({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, aggs, })](./kibana-plugin-core-server.savedobjectsrepository.find.md) | | |
| [get(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.get.md) | | Gets a single object |
| [incrementCounter(type, id, counterFieldName, options)](./kibana-plugin-core-server.savedobjectsrepository.incrementcounter.md) | | Increases a counter field by one. Creates the document if one doesn't exist for the given id. |
| [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsrepository.update.md) | | Updates an object |
Expand Down
10 changes: 7 additions & 3 deletions src/core/public/saved_objects/saved_objects_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ export interface SavedObjectsBatchResponse<T = unknown> {
*
* @public
*/
export interface SavedObjectsFindResponsePublic<T = unknown> extends SavedObjectsBatchResponse<T> {
export interface SavedObjectsFindResponsePublic<T = unknown, A = unknown>
extends SavedObjectsBatchResponse<T> {
aggregations?: A;
total: number;
perPage: number;
page: number;
Expand Down Expand Up @@ -291,7 +293,7 @@ export class SavedObjectsClient {
* @property {object} [options.hasReference] - { type, id }
* @returns A find result with objects matching the specified search.
*/
public find = <T = unknown>(
public find = <T = unknown, A = unknown>(
options: SavedObjectsFindOptions
): Promise<SavedObjectsFindResponsePublic<T>> => {
const path = this.getPath(['_find']);
Expand All @@ -306,6 +308,7 @@ export class SavedObjectsClient {
sortField: 'sort_field',
type: 'type',
filter: 'filter',
aggs: 'aggs',
};

const renamedQuery = renameKeys<SavedObjectsFindOptions, any>(renameMap, options);
Expand All @@ -322,13 +325,14 @@ export class SavedObjectsClient {
SavedObjectsFindResponsePublic
>(
{
aggregations: 'aggregations',
XavierM marked this conversation as resolved.
Show resolved Hide resolved
saved_objects: 'savedObjects',
total: 'total',
per_page: 'perPage',
page: 'page',
},
resp
) as SavedObjectsFindResponsePublic<T>;
) as SavedObjectsFindResponsePublic<T, A>;
});
};

Expand Down
2 changes: 2 additions & 0 deletions src/core/server/saved_objects/routes/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const registerFindRoute = (router: IRouter) => {
),
fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
filter: schema.maybe(schema.string()),
aggs: schema.maybe(schema.string()),
}),
},
},
Expand All @@ -62,6 +63,7 @@ export const registerFindRoute = (router: IRouter) => {
hasReference: query.has_reference,
fields: typeof query.fields === 'string' ? [query.fields] : query.fields,
filter: query.filter,
aggs: query.aggs,
});

return res.ok({ body: result });
Expand Down
103 changes: 103 additions & 0 deletions src/core/server/saved_objects/service/lib/aggs_utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* 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 { validateGetSavedObjectAggs } from './aggs_utils';
import { mockMappings } from './filter_utils.test';

describe('Filter Utils', () => {
pgayvallet marked this conversation as resolved.
Show resolved Hide resolved
describe('#validateGetSavedObjectAggs', () => {
test('Validate a simple aggregations', () => {
expect(
validateGetSavedObjectAggs(
['foo'],
{ aggName: { max: { field: 'foo.attributes.bytes' } } },
mockMappings
)
).toEqual({
aggName: {
max: {
field: 'foo.bytes',
},
},
});
});
test('Validate a nested simple aggregations', () => {
expect(
validateGetSavedObjectAggs(
['alert'],
{ aggName: { cardinality: { field: 'alert.attributes.actions.group' } } },
mockMappings
)
).toEqual({
aggName: {
cardinality: {
field: 'alert.actions.group',
},
},
});
});

test('Throw an error when types is not allowed', () => {
expect(() => {
validateGetSavedObjectAggs(
['alert'],
{
aggName: {
max: { field: 'foo.attributes.bytes' },
},
},
mockMappings
);
}).toThrowErrorMatchingInlineSnapshot(`"This type foo is not allowed: Bad Request"`);
});

test('Throw an error when aggregation is not defined in SavedObjectAggs', () => {
expect(() => {
validateGetSavedObjectAggs(
['foo'],
{
aggName: {
MySuperAgg: { field: 'foo.attributes.bytes' },
},
},
mockMappings
);
}).toThrowErrorMatchingInlineSnapshot(
`"Invalid value {\\"aggName\\":{\\"MySuperAgg\\":{\\"field\\":\\"foo.attributes.bytes\\"}}} supplied to : { [K in string]: ((Partial<{ filter: { term: { [K in string]: string } }, histogram: ({ field: string } & { interval: number } & Partial<{ min_doc_count: number, extended_bounds: { min: number, max: number }, keyed: boolean, missing: number, order: { [K in string]: desc } }>), terms: ({ field: string } & Partial<{ field: string, size: number, show_term_doc_count_error: boolean, order: { [K in string]: desc } }>) }> & Partial<{ avg: { field: string }, weighted_avg: ({ value: ({ field: string } & Partial<{ missing: number }>), weight: ({ field: string } & Partial<{ missing: number }>) } & Partial<{ format: string, value_type: string }>), cardinality: { field: string }, max: ({ field: string } & Partial<{ missing: number }>), min: ({ field: string } & Partial<{ missing: number }>), top_hits: Partial<{ explain: boolean, from: string, highlight: any, seq_no_primary_term: boolean, size: number, sort: any, stored_fields: Array<string>, version: boolean, _name: string, _source: Partial<{ includes: Array<string>, excludes: Array<string> }> }> }>) & Partial<{ aggs: (Partial<{ filter: { term: { [K in string]: string } }, histogram: ({ field: string } & { interval: number } & Partial<{ min_doc_count: number, extended_bounds: { min: number, max: number }, keyed: boolean, missing: number, order: { [K in string]: desc } }>), terms: ({ field: string } & Partial<{ field: string, size: number, show_term_doc_count_error: boolean, order: { [K in string]: desc } }>) }> & Partial<{ avg: { field: string }, weighted_avg: ({ value: ({ field: string } & Partial<{ missing: number }>), weight: ({ field: string } & Partial<{ missing: number }>) } & Partial<{ format: string, value_type: string }>), cardinality: { field: string }, max: ({ field: string } & Partial<{ missing: number }>), min: ({ field: string } & Partial<{ missing: number }>), top_hits: Partial<{ explain: boolean, from: string, highlight: any, seq_no_primary_term: boolean, size: number, sort: any, stored_fields: Array<string>, version: boolean, _name: string, _source: Partial<{ includes: Array<string>, excludes: Array<string> }> }> }>) }>) }, excess properties: [\\"MySuperAgg\\"]: Bad Request"`
XavierM marked this conversation as resolved.
Show resolved Hide resolved
);
});

test('Throw an error when you add attributes who are not defined in SavedObjectAggs', () => {
expect(() => {
validateGetSavedObjectAggs(
['alert'],
{
aggName: {
cardinality: { field: 'alert.attributes.actions.group' },
script: 'I want to access that I should not',
},
},
mockMappings
);
}).toThrowErrorMatchingInlineSnapshot(
`"Invalid value {\\"aggName\\":{\\"cardinality\\":{\\"field\\":\\"alert.attributes.actions.group\\"},\\"script\\":\\"I want to access that I should not\\"}} supplied to : { [K in string]: ((Partial<{ filter: { term: { [K in string]: string } }, histogram: ({ field: string } & { interval: number } & Partial<{ min_doc_count: number, extended_bounds: { min: number, max: number }, keyed: boolean, missing: number, order: { [K in string]: desc } }>), terms: ({ field: string } & Partial<{ field: string, size: number, show_term_doc_count_error: boolean, order: { [K in string]: desc } }>) }> & Partial<{ avg: { field: string }, weighted_avg: ({ value: ({ field: string } & Partial<{ missing: number }>), weight: ({ field: string } & Partial<{ missing: number }>) } & Partial<{ format: string, value_type: string }>), cardinality: { field: string }, max: ({ field: string } & Partial<{ missing: number }>), min: ({ field: string } & Partial<{ missing: number }>), top_hits: Partial<{ explain: boolean, from: string, highlight: any, seq_no_primary_term: boolean, size: number, sort: any, stored_fields: Array<string>, version: boolean, _name: string, _source: Partial<{ includes: Array<string>, excludes: Array<string> }> }> }>) & Partial<{ aggs: (Partial<{ filter: { term: { [K in string]: string } }, histogram: ({ field: string } & { interval: number } & Partial<{ min_doc_count: number, extended_bounds: { min: number, max: number }, keyed: boolean, missing: number, order: { [K in string]: desc } }>), terms: ({ field: string } & Partial<{ field: string, size: number, show_term_doc_count_error: boolean, order: { [K in string]: desc } }>) }> & Partial<{ avg: { field: string }, weighted_avg: ({ value: ({ field: string } & Partial<{ missing: number }>), weight: ({ field: string } & Partial<{ missing: number }>) } & Partial<{ format: string, value_type: string }>), cardinality: { field: string }, max: ({ field: string } & Partial<{ missing: number }>), min: ({ field: string } & Partial<{ missing: number }>), top_hits: Partial<{ explain: boolean, from: string, highlight: any, seq_no_primary_term: boolean, size: number, sort: any, stored_fields: Array<string>, version: boolean, _name: string, _source: Partial<{ includes: Array<string>, excludes: Array<string> }> }> }>) }>) }, excess properties: [\\"script\\"]: Bad Request"`
);
});
});
});
58 changes: 58 additions & 0 deletions src/core/server/saved_objects/service/lib/aggs_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 { IndexMapping } from '../../mappings';

import { SavedObjectsErrorHelpers } from './errors';
import { hasFilterKeyError } from './filter_utils';
import { SavedObjectAggs, validateSavedObjectTypeAggs } from './saved_object_aggs_types';

export const validateGetSavedObjectAggs = (
allowedTypes: string[],
aggs: SavedObjectAggs,
indexMapping: IndexMapping
): SavedObjectAggs => {
validateSavedObjectTypeAggs(aggs);
return validateGetAggFieldValue(allowedTypes, aggs, indexMapping);
};

const validateGetAggFieldValue = (
allowedTypes: string[],
aggs: any,
indexMapping: IndexMapping
): SavedObjectAggs => {
return Object.keys(aggs).reduce((acc, key) => {
pgayvallet marked this conversation as resolved.
Show resolved Hide resolved
if (key === 'field') {
XavierM marked this conversation as resolved.
Show resolved Hide resolved
const error = hasFilterKeyError(aggs[key], allowedTypes, indexMapping);
XavierM marked this conversation as resolved.
Show resolved Hide resolved
if (error != null) {
throw SavedObjectsErrorHelpers.createBadRequestError(error);
}
return {
...acc,
[key]: aggs[key].replace('.attributes', ''),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Quick thoughts about multi-types aggregation: I think that, with current implementation, allowing to use aggregations when using find with multiple types does not make any sense, as we enforce that the fields we are performing the aggregations against follow the {type}.attributes.{field} format. I don't see what kind of aggregations we could be performing when searching for multiple types with this 'limitation', as each type got their own isolated attributes?

Should we just throw an error when a developers tries to use aggregations when using find with multiple types? Or should we just not care and let him do?

Second question, in the opposite direction: we we want to allow to perform aggregations on 'global' fields at some point (not asking for this PR, but a mid/long term)? such as

aggs: {
    by_type: {
        terms: {
          field: 'type',
        },
    }
}

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 think we should just not care but if you all feel strongly about it, I can add it

} else if (typeof aggs[key] === 'object') {
return { ...acc, [key]: validateGetAggFieldValue(allowedTypes, aggs[key], indexMapping) };
}
return {
...acc,
[key]: aggs[key],
};
}, {});
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { esKuery } from '../../../../../plugins/data/server';

import { validateFilterKueryNode, validateConvertFilterToKueryNode } from './filter_utils';

const mockMappings = {
export const mockMappings = {
pgayvallet marked this conversation as resolved.
Show resolved Hide resolved
properties: {
updatedAt: {
XavierM marked this conversation as resolved.
Show resolved Hide resolved
type: 'date',
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/saved_objects/service/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ export {
} from './scoped_client_provider';

export { SavedObjectsErrorHelpers } from './errors';

export { SavedObjectAggs } from './saved_object_aggs_types';
Loading