-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming this to mergeNodeBuckets
?
}, | ||
}; | ||
|
||
let response = await framework.callWithRequest<any, any>(request, 'search', query); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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]);
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Thanks.
import { getIntervalInSeconds } from '../../utils/get_interval_in_seconds'; | ||
import { InfraSnapshotRequestOptions } from './snapshot'; | ||
|
||
export interface InfraSnapshotMetricResponse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we rename this to InfraSnapshotNodeBucket
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
Summary
First part of implementation for #31405 -- add a new graphql endpoint with the new behaviour.
This is mostly a rewrite of the existing
nodes
endpoint. 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:
adapter
anddomain
code as we agreed we don't need this abstraction any moreInfraSnapshot*
Testing
Test in GraphiQL.
Sample query (replace
FROM
,TO
andHOSTNAME
)SNAPSHOT_COMPOSITE_REQUEST_SIZE
inserver/lib/snapshot/constants.ts
to a number smaller than your number of nodeselasticsearch.logQueries: true
andlogging.verbose: true
) to verify that multiple queries are indeed performedgroupBy
s, 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.