-
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
[ML] Anomaly Detection: Fix anomaly marker click in Single Metric Viewer embeddable #176788
[ML] Anomaly Detection: Fix anomaly marker click in Single Metric Viewer embeddable #176788
Conversation
Pinging @elastic/ml-ui (:ml) |
if (dataViewsService === null) { | ||
throw new Error('Data views are not initialized!'); | ||
} | ||
export async function getDataViewIdFromName( |
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.
Would it be worth putting these functions in a provider which takes the dataViewsService
?
I see there are a few places where this is called multiple times in the same file.
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.
@walterra, @jgowdyelastic - this is already done via indexServiceFactory
in ml/public/application/util/index_service.ts
- this file will eventually replace this utils file.
Now I'm thinking it would have been clearer to add a comment to this soon-to-be-deprecated file saying it will be replaced by the one I mentioned above and that that one should be used.
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.
Luckily all remaining usages of this function were in places where we could make the new service easily available so I was able to get rid of this stand-alone function in 70ab770. 🥳
const httpService = new HttpService(httpStart); | ||
const mlApiServices = mlApiServicesProvider(httpService); | ||
const mlIndexUtils = indexServiceFactory(dataViews); | ||
const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils); |
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.
To prevent bloat of the context, perhaps the mlFieldFormatService
should be created in the component as needed and then passed/made available to it's children?
For example, we already have mlApiServices
and dataViews
in context, I believe.
fieldFormatServiceFactory
requires mlApiServices
and mlIndexUtils
- which, in turn, only requires the dataViewsService.
From that, fieldFormatService could be instantiated once in the component in which it is needed instead of adding more to the context.
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.
Not sure it's bloat? Prior to this PR the service was only instantiated once too, it's just that with the dependency cache under the hood it could be imported everywhere without being passed around via context. So this variant felt closer to the original then instantiating multiple times. Note the factory doesn't have any costly side effects, for example it doesn't call any requests, that's only happening once you call one of its methods.
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 tend to agree that it would be better to keep mlIndexUtils
and mlFieldFormatService
out of this context as they are used so rarely throughout our plugin. It sets a precedence that all utils and services we have should go in here too.
Instantiating them when needed in each file would be better IMO.
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.
As discussed offline, I'm leaving mlFieldFormatService
here as a global service for now because of its internal state. mlIndexUtils
will not be passed on to global services, instead it can be accessed within components via useMlIndexUtils()
.
I added the following comment to each place where the services are set up (the main ML app, the anomaly charts embeddable and the single metric viewer embeddable):
// Note on the following services:
// - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`,
// but it's not being made available as part of global services. Since it's just
// some stateless utils `useMlIndexUtils()` should be used from within components.
// - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache",
// so because of its own state it needs to be made available as a global service.
// In the long run we should again try to get rid of it here and make it available via
// its own context or possibly without having a singleton like state at all, since the
// way this manages its own state right now doesn't consider React component lifecycles.
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.
Does this still require a code change? mlIndexUtils
is still being added to the context.
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.
Oh yes, overlooked that one, thanks! Fixed in bccd244.
@@ -104,7 +104,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { | |||
|
|||
const getAnomaliesMapsLink = async (anomaly: MlAnomaliesTableRecord) => { | |||
const index = job.datafeed_config.indices[0]; | |||
const dataViewId = await getDataViewIdFromName(index); | |||
const dataViewId = await getDataViewIdFromName(data.dataViews, index); |
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.
As mentioned after James's comment below - the indexServiceFactory
in ml/public/application/util/index_service.ts
(which is meant to replace the utils file getDataViewIdFromName
is pulled from here) should be used here to get that functionality. It only requires dataViewsService
, which should be available in this component.
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.
Updated in 70ab770.
@@ -97,7 +97,7 @@ export const useResultsViewConfig = (jobId: string) => { | |||
|
|||
try { | |||
const destIndex = getDestinationIndex(jobConfigUpdate); | |||
const destDataViewId = (await getDataViewIdFromName(destIndex)) ?? destIndex; | |||
const destDataViewId = (await getDataViewIdFromName(dataViews, destIndex)) ?? destIndex; |
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.
Same note here for either instantiating indexServiceFactory
with the necessary services available via useMlKibana
or passing in the service or function after instantiation wherever this hook is used.
This is the long term solution @jgowdyelastic and I chatted about for moving away from the dependency cache and also preventing making the context too bloated.
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.
Updated in 70ab770.
@@ -138,7 +139,7 @@ export const Controls: FC<Props> = React.memo( | |||
const nodeType = selectedNode?.data('type'); | |||
|
|||
const onCreateJobClick = useCallback(async () => { | |||
const dataViewId = await getDataViewIdFromName(nodeLabel); | |||
const dataViewId = await getDataViewIdFromName(data.dataViews, nodeLabel); |
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.
Same here
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.
Updated in 70ab770.
useEffect(() => { | ||
const explorerService = explorerServiceFactory(mlFieldFormatService); |
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.
Same note here - mlFieldFormatService
doesn't need to be pulled from context but can be instantiated in this component once (using the required services already in context) to be passed on.
@@ -83,7 +85,10 @@ export class ExplorerChartDistribution extends React.Component { | |||
return; | |||
} | |||
|
|||
const fieldFormat = mlFieldFormatService.getFieldFormat(config.jobId, config.detectorIndex); | |||
const fieldFormat = this.context.services.mlServices.mlFieldFormatService.getFieldFormat( |
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.
Same here for instantiating the fieldformatService on the fly.
@alvarezmelissa87 Thanks for the testing and spotting the errors!
|
Pinging @elastic/obs-ux-infra_services-team (Team:obs-ux-infra_services) |
…kibana into 176651-ml-smv-embeddable-click
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Async chunks
Page load bundle
Unknown metric groupsasync chunk count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: cc @walterra |
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.
Tested and LGTM
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.
LGTM
Fixing bug introduced in #176788 The `jobService` hasn't been initialised when being used to load the groups.
Summary
Part of #176651 and #153476. Follow up to #175556.
Fixes the click on anomaly markers in the Single Metric Viewer embeddable. This required refactoring some dependencies to be properly passed in via React contexts instead of the legacy dependency cache.
smv-embeddable-click-0001.webm
Checklist