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

Expression datatable formatting hints #43091

Merged
merged 12 commits into from
Aug 21, 2019

Conversation

flash1293
Copy link
Contributor

This PR adds an optional property to the column of expression data tables: formatHint. It carries the JSON representation of a field formatter config that can be used by expression renderers to format the values of the column correctly.

This is a design decision with trade-offs: Conceptually formatting belongs to the presentation of the data and shouldn't be part of the "data source"-ey expression functions. But correctly formatting something is very strongly coupled to the source of a given data table - the data source (in this case esaggs) knows about source-specific formatting information (e.g. user defined field formatters on the given index pattern or the interval of a date_histogram aggregation to format dates accordingly). The render functions shouldn't know or care about these concepts and passing in the formatting information as sub-expressions of the renderer function would cause redundant information on the expression that can get out of sync. Providing the data source with a way to pass formatting hints along with the data itself together with a way to overwrite/ignore this formatting offers the benefits of both ways - a clean and robust expression that integrates with existing formatting sources of the system in the standard case, but still full control over the formatting if this is necessary.

The esaggs function is extended with an optional boolean argument includeFormatHints that can be set to add formatting information to the data table returned by esaggs. It uses the same implementation as the build_pipeline helper for the expression loader for Visualize.

Theoretically the Visualize renderers and build_pipeline could be rewritten to not include the formatting information in the arguments of the render function but to use the format hints from esaggs, but that's out of scope for this PR IMHO.

Not 100% sure about the files the various helpers and types should reside, open for suggestions here.

@flash1293 flash1293 added v8.0.0 release_note:skip Skip the PR/issue when compiling release notes v7.4.0 labels Aug 12, 2019
@flash1293 flash1293 requested a review from a team as a code owner August 12, 2019 10:11
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Member

@lukeelmers lukeelmers left a comment

Choose a reason for hiding this comment

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

Overall LGTM, just added a few questions & comments.

@@ -21,9 +21,15 @@ import { map } from 'lodash';

const name = 'kibana_datatable';

export interface SerializedFieldFormat<TParams = object> {
Copy link
Member

Choose a reason for hiding this comment

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

Since this is public/exported, maybe it would make sense to co-locate it with getFormat & the pipeline utilities, and import it into here instead? Since it doesn't have anything to do with the kibana_datatable type specifically.

To some extent it doesn't matter a lot right now since all of the ui/visualize/loader stuff is going away or moving eventually anyway, but IMO this is the only thing in the PR that feels like maybe it should be moved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's how I started out over here (5548579), but somehow the type check fails in completely unrelated places if I do that - something about importing legacy stuff from the new world. Then I thought it's maybe even cleaner that way because the types of the data table are all defined in one place.

Maybe this was a configuration error already fixed, I'll move it over and check.

Copy link
Member

@lukeelmers lukeelmers Aug 20, 2019

Choose a reason for hiding this comment

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

Ah I didn't think about that @flash1293 -- you are totally right. You can import new platform into legacy, but you cannot import legacy into new platform.

Not sure what makes the most then, since we definitely won't want to keep this in with the datatable type longer term. Maybe instead we should stick it in the types/common.ts file for now? I just don't want it to get lost in expression_types/kibana_datatable.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved it over to common

name: column.name,
})),
columns: response.columns.map((column: any) => {
const cleanedColumn: KibanaDatatable['columns'][0] = {
Copy link
Member

Choose a reason for hiding this comment

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

Do you think enough folks will have a use case for this that it'd be worth exporting Column separately, perhaps as KibanaDatatableColumn, so that you don't have to do stuff like this?

Copy link
Member

Choose a reason for hiding this comment

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

why do we always take the first column ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because all of the columns in the array have the same type, this is a hack. I agree with @lukeelmers here, it makes sense to expose KibanaDatatableColumn to be able to cleanly reference this type.

Current solution works like this:
KibanaDatatable is of type { columns: Array<Column> }
KibanaDatatable['columns] is of type Array<Column>

now the question is how do I get to the type of the Column itself? I could do a proper type inference like this: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types or I could do a dirty hack and get the type of the 0 property of KibanaDatatable['columns] which happens to be Column

Copy link
Member

Choose a reason for hiding this comment

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

A bit orthogonal to the discussion here, but in cases where magic like this is necessary, I find the syntax KibanaDatatable['columns'][number] to be slightly easier to grok, mostly because the index number can be distracting as @ppisljar points out.

But agreed that exporting separately feels like the way to go here.

@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-app-arch

@lukeelmers lukeelmers added review Feature:ExpressionLanguage Interpreter expression language (aka canvas pipeline) labels Aug 19, 2019
Copy link
Member

@ppisljar ppisljar left a comment

Choose a reason for hiding this comment

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

code LGTM

name: column.name,
})),
columns: response.columns.map((column: any) => {
const cleanedColumn: KibanaDatatable['columns'][0] = {
Copy link
Member

Choose a reason for hiding this comment

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

why do we always take the first column ?

timeRange: get(context, 'timeRange', null),
query: get(context, 'query', null),
filters: get(context, 'filters', null),
timeRange: get(context, 'timeRange', undefined),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this went in because the existing RequestHandler types don't allow null here (but they allow undefined)

@@ -42,7 +42,7 @@ export interface RequestHandlerParams {
visParams?: any;
}

export type RequestHandler = <T>(params: RequestHandlerParams) => T;
export type RequestHandler<T = unknown> = (params: RequestHandlerParams) => T;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moving the type parameter to the outer type to be able to specify it in courier-specific type. Inferred types in other usages should work like before.

@elasticmachine
Copy link
Contributor

💔 Build Failed

@flash1293
Copy link
Contributor Author

@lukeelmers :this: is the error I meant - no idea why this happens but if we don't import legacy from within NP world, it works.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:ExpressionLanguage Interpreter expression language (aka canvas pipeline) release_note:skip Skip the PR/issue when compiling release notes review v7.4.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants