Repository navigation
[Infra UI] Add new graphql endpoint for snapshot data - #34264
Conversation
9e98ac0 to
04e1ad5
Compare
💚 Build Succeeded |
|
Pinging @elastic/infrastructure-ui |
04e1ad5 to
d4da0f0
Compare
💚 Build Succeeded |
d4da0f0 to
bca3954
Compare
💔 Build Failed |
0043d20 to
469f1c4
Compare
💔 Build Failed |
💔 Build Failed |
469f1c4 to
065e53b
Compare
💚 Build Succeeded |
💔 Build Failed |
💔 Build Failed |
|
jenkins, please test this |
💚 Build Succeeded |
simianhacker
left a comment
There was a problem hiding this comment.
I left some notes inline with the code...
Also, I would like to see an integration test for this as well. It looks like the original map test is tied directly with the UI query which makes it difficult to copy. I would probably copy it and modify it to use a hard coded query. Then update it to be consistent once the UI portion is available. I would leave a TODO comment next to the hard coded query OR file a follow up issue so we don't forget.
| }; | ||
|
|
||
| const mergeNodeMetrics = ( | ||
| nodes: any[], |
There was a problem hiding this comment.
Both nodes and metrics should use InfraSnapshotMetricResponse[] as the type. Also I would change the names to nodeBuckets and nodeBucketsWithMetrics.
There was a problem hiding this comment.
They have different types, but you're right nevertheless.
| const result: any[] = []; | ||
| const nodeMetricsForLookup = getNodeMetricsForLookup(metrics); | ||
|
|
||
| nodes.forEach(node => { |
There was a problem hiding this comment.
Please change this to a map so that it doesn't side effect result and just return directly from the map. This will eliminate the any on line 186 as well.
| return buckets; | ||
| }; | ||
|
|
||
| const mergeNodeMetrics = ( |
There was a problem hiding this comment.
How about renaming this to mergeNodeBuckets?
| }, | ||
| }; | ||
|
|
||
| let response = await framework.callWithRequest<any, any>(request, 'search', query); |
There was a problem hiding this comment.
Lines 94-108 are identical to lines 164-178. How about we create a function named getAllNodeBuckets which return InfraSnapshotMetricResponse[]? I also think we need to defined the Aggregation response type and pass it to the second type argument on the function call, something like:
interface InfraSnapshotAggregation {
buckets: InfraSnapshotMetricResponse[];
after_key: { [id: string]: string };
}
interface InfraSnapshotAggregationResponse {
nodes: InfraSnapshotAggregation;
}
const response = framework.callWithRequest<{}, InfraSnapshotAggregationResponse>(request, 'search', query);When you provide an aggregation type, the generics that back up that request will return a type that defines the aggregation key as optional which will then require a check to make sure aggregations are return. We can't count on the response to always have that key set.
I would also like to see the let and side effecting while loop factored out of this code. If you combine lines 94-108 and 164-178 in to one function. You could write that function to recurse until there are no more results, then return all the buckets. Maybe something like this:
const getAllNodesBuckets = async (
framework: InfraBackendFrameworkAdapter,
req: InfraFrameworkRequest,
requestQuery: InfraSnapshotNodeRequest, // this type needs to be created
previousBuckets: InfraSnapshotMetricResponse[] = []
): Promise<InfraSnapshotMetricResponse[]> => {
const response = await framework.callWithRequest<{}, InfraSnapshotAggregationResponse>(
req,
'search',
requestQuery
);
// Nothing available, return the previous buckets.
if (response.hits.total.value === 0) {
return previousBuckets;
}
// if ES doesn't return aggregations key, we can either throw an error or return previousBuckets.
if (!response.aggregations) {
throw new Error('Whoops!, `aggregations` key must always be returned.');
}
const currentBuckets = response.aggregations.nodes.buckets;
// if the buckets length is zero then we are finished paginating through the results
if (currentBuckets.length === 0) {
return previousBuckets;
}
// create a new request object
const newQuery = {
...requestQuery,
body: {
...requestQuery.body,
aggs: {
...requestQuery.body.aggs,
nodes: {
...requestQuery.body.aggs.nodes,
composite: {
...requestQuery.body.aggs.nodes.composite,
after: response.aggregations.nodes.after_key,
},
},
},
},
};
// request additional buckets
return getAllNodesBuckets(framework, req, newQuery, [...previousBuckets, ...currentBuckets]);
};| import { getIntervalInSeconds } from '../../utils/get_interval_in_seconds'; | ||
| import { InfraSnapshotRequestOptions } from './snapshot'; | ||
|
|
||
| export interface InfraSnapshotMetricResponse { |
There was a problem hiding this comment.
How about we rename this to InfraSnapshotNodeBucket
There was a problem hiding this comment.
Good point. I'll refine the type names in that file.
|
|
||
| export const getGroupedNodesSources = (options: InfraSnapshotRequestOptions) => { | ||
| const sources = []; | ||
| options.groupby.forEach(gb => { |
There was a problem hiding this comment.
How about we use a map here with a trailing push for the last source definition and then return the results directly from the function? This needs a return type as well.
There was a problem hiding this comment.
Agreed on the map, but I'm not sure about the return type -- this function is used only once and only factored out for readability. Adding an explicit type only adds mental overload here in my opinion.
There was a problem hiding this comment.
I only recommended the type because Typescript will likely complain about the object/key signature. If it works without the type then it should be fine.
| ) => { | ||
| const path = []; | ||
| const node = groupBucket.key; | ||
| options.groupby.forEach(gb => { |
There was a problem hiding this comment.
Change this to a map with a push for the trailing value and return directly from the function.
|
|
||
| export const getNodeMetricsForLookup = (metrics: InfraSnapshotMetricResponse[]) => { | ||
| const nodeMetricsForLookup: any = {}; | ||
| metrics.forEach(metric => { |
There was a problem hiding this comment.
How about we use a reduce here to transform the array into a hash? Also we should define a return type for the parent function.
| "Nodes of type host, container or pod grouped by 0, 1 or 2 terms" | ||
| nodes( | ||
| type: InfraNodeType! | ||
| groupby: [InfraSnapshotGroupbyInput!]! |
There was a problem hiding this comment.
I would change groupby to groupBy to be consistent
| groupby: [InfraSnapshotGroupbyInput!]! | |
| groupBy: [InfraSnapshotGroupbyInput!]! |
💚 Build Succeeded |
💚 Build Succeeded |
That's why I wrote in the original description:
I'm actually already working on these tests, but it will be easier to add them in the next PR (which is based on this one). Would this work for you? |
9daf16d to
c475797
Compare
💚 Build Succeeded |
|
@simianhacker thanks for your review! I've responded to all your comments, this is ready for another look. |
simianhacker
left a comment
There was a problem hiding this comment.
Thanks for the changes. Good Job!
___ ________ _________ _____ ______
|\ \ |\ ____\\___ ___\\ _ \ _ \
\ \ \ \ \ \___\|___ \ \_\ \ \\\__\ \ \
\ \ \ \ \ \ ___ \ \ \ \ \ \\|__| \ \
\ \ \____\ \ \|\ \ \ \ \ \ \ \ \ \ \
\ \_______\ \_______\ \ \__\ \ \__\ \ \__\
\|_______|\|_______| \|__| \|__| \|__|
* Add new graphql endpoint for snapshot data * Polishing. * Keep type generic that is used outside snapshots * Keep one more generic type generic * Use camelCase for consistency. * Refine type names * Add return types. * Use idiomatic javascript. * Factor out getAllCompositeAggregationData<T>() * Refine naming. * More idiomatic JavaScript, more types.
…35792) * [Infra UI] Add new graphql endpoint for snapshot data (#34264) * Add new graphql endpoint for snapshot data * Polishing. * Keep type generic that is used outside snapshots * Keep one more generic type generic * Use camelCase for consistency. * Refine type names * Add return types. * Use idiomatic javascript. * Factor out getAllCompositeAggregationData<T>() * Refine naming. * More idiomatic JavaScript, more types. * Use pre-8.x response format (see also #30826)
* Add new graphql endpoint for snapshot data * Polishing. * Keep type generic that is used outside snapshots * Keep one more generic type generic * Use camelCase for consistency. * Refine type names * Add return types. * Use idiomatic javascript. * Factor out getAllCompositeAggregationData<T>() * Refine naming. * More idiomatic JavaScript, more types.
Summary
First part of implementation for #31405 -- add a new graphql endpoint with the new behaviour.
This is mostly a rewrite of the existing
nodesendpoint. Once this change has been merged, the UI will be changed to use this new endpoint, and the old one can be removed. This will happen in #34938 .Notable changes:
adapteranddomaincode as we agreed we don't need this abstraction any moreInfraSnapshot*Testing
Test in GraphiQL.
Sample query (replace
FROM,TOandHOSTNAME)SNAPSHOT_COMPOSITE_REQUEST_SIZEinserver/lib/snapshot/constants.tsto a number smaller than your number of nodeselasticsearch.logQueries: trueandlogging.verbose: true) to verify that multiple queries are indeed performedgroupBys, metric types and filter queriesChecklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.- [ ] This was checked for cross-browser compatibility, including a check against IE11- [ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support- [ ] Documentation was added for features that require explanation or tutorials- [ ] Unit or functional tests were updated or added to match the most common scenariosI would like to add tests in the implementation of #34718 because we use the query definitions from the UI containers in our functional tests, and this is where they will be added.
- [ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers
- [ ] This was checked for breaking API changes and was labeled appropriately- [ ] This includes a feature addition or change that requires a release note and was labeled appropriatelyThis is ready for review now, or together with #34938 -- whichever you prefer.