-
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
[Ingest pipelines] Upload indexed document to test a pipeline #77939
[Ingest pipelines] Upload indexed document to test a pipeline #77939
Conversation
6b36ab5
to
ef10904
Compare
Pinging @elastic/es-ui (Team:Elasticsearch UI) |
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.
🔥 This is awesome! I tested locally and the UX and code both look solid to me. I had some suggestions for enhancing the UX and making the code a bit more robust around some corner cases, but nothing worth blocking on.
Does it make sense to have a single form to import documents (particularly in the case where you might want to add more than one document)?
I found this UX to be fairly intuitive. I think it's useful and works well. I think adding a list of multiple indices and documents would add unnecessary complexity.
Thoughts on having the imported document populate the code editor below?
This was a really cool interaction! It was straightforward to load up documents and then find and review them in the code editor. I think we can provide more feedback when a document is successfully loaded, per one of my comments below.
'DISCOVER_APP_URL_GENERATOR' | ||
); | ||
const discoverUrl = await discoverUrlGenerator.createUrl({ indexPatternId: undefined }); | ||
setDiscoverLink(discoverUrl); |
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.
It seems possible for the component to be mounted at the time getDiscoverUrl
is called, but unmounted by time time discoverUrl
resolves. In that case, don't we want to move the umounted check a little later in the control flow?
const getDiscoverUrl = async (): Promise<void> => {
const discoverUrlGenerator = services.urlGenerators.getUrlGenerator(
'DISCOVER_APP_URL_GENERATOR'
);
const discoverUrl = await discoverUrlGenerator.createUrl({ indexPatternId: undefined });
if (!unmounted) {
setDiscoverLink(discoverUrl);
}
};
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'm not very familiar with how our URL generators work, so maybe this is a dumb question. What happens if Discover is disabled? Will discoverUrlGenerator
or maybe discoverUrl
resolve to undefined
? In which case should we check that it's defined before consuming them?
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 bringing this up!
I read through the docs again, and there is a isDeprecated
flag that is returned from the url generator. I added a check for this in the code.
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.
TIL about the isDeprecated
flag! I tested this out locally by setting discover.enabled: false
and I ended up getting a console error:
I read the docs a bit more closely and I think isDeprecated
is intended to address the situation when an app has changed the way it stores state in its URLs, like the way Discover stores filter, query, and sort state in the URL. I'm guessing the above error is the intended way they surface a generator missing due to a plugin being disabled. In this case, I suggest use a try/catch to detect the scenario and handle it gracefully. Here's an example of what I mean, though ideally we'd inspect the type of error and bubble it up if it's not a no generator found
error:
const getDiscoverUrl = async (): Promise<void> => {
let isDeprecated;
let createUrl;
try {
({ isDeprecated, createUrl } = services.urlGenerators.getUrlGenerator(
'DISCOVER_APP_URL_GENERATOR'
));
} catch(e) {
// Discover is not enabled.
setDiscoverLink(undefined);
return;
}
if (isDeprecated) {
setDiscoverLink(undefined);
return;
}
const discoverUrl = await createUrl({ indexPatternId: undefined });
if (isMounted.current) {
setDiscoverLink(discoverUrl);
}
};
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 created #78344 to improve the URL generator docs or API for this scenario.
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.
Ah, I see. Thanks for looking into this more closely! I've updated the code and added a comment with a link to the issue you created.
), | ||
indexField: { | ||
fieldLabel: i18n.translate( | ||
'xpack.ingestPipelines.pipelineEditor.loadDocuments.table.indexColumnLabel', |
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.
Just wondering why table
and column
are used in the IDs here and on line 63?
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.
Oops! I started with a slightly different design, and forgot to update the the IDs here.
} | ||
), | ||
validationMessage: i18n.translate( | ||
'xpack.ingestPipelines.pipelineEditor.loadDocuments.idRequiredError', |
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.
Nit: should be idRequiredErrorMessage
based on the guidelines.
} | ||
), | ||
validationMessage: i18n.translate( | ||
'xpack.ingestPipelines.pipelineEditor.loadDocuments.indexRequiredError', |
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.
Nit: should be indexRequiredErrorMessage
based on the guidelines.
} | ||
|
||
onAddDocuments(document); | ||
form.reset(); |
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.
If you're adding multiple documents from the same index, it can be useful to retain the form state so you don't need to keep re-entering the index. Similarly, if the IDs happen to be sequential I can imagine it being useful to retain the document ID so you can just increment them easily. At the very least it's helpful to see which document you just added so you can refer to it when choosing the next one. For these reasons I suggest retaining the form state and removing this reset.
I think it would be helpful feedback to surface a success callout just above the form when the request is successful, like this:
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.
|
||
const onAddDocumentHandler = useCallback( | ||
(document) => { | ||
const { documents: existingDocuments } = formatData(); |
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.
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.
Great catch!
setIsLoadingDocument(true); | ||
setLoadingDocumentError(undefined); | ||
|
||
const { error, data: document } = await services.api.loadDocument(index, id); |
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.
If the network's a bit slow and the user navigates away before this request resolves, then we'll get an error about updating state on an unmounted component:
It's a bit verbose, but here's one way we can solve this problem. We can track unmounted state like this:
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);
Then we can check this state before updating any state after the request resolves:
if (isMounted.current) {
setIsLoadingDocument(false);
if (error) {
setLoadingDocumentError(error);
return;
}
onAddDocuments(document);
form.reset();
}
To reduce the verbosity we could extract the logic for tracking isMounted state into its own hook per the last few comments on this discussion: https://gist.github.com/jaydenseric/a67cfb1b809b1b789daa17dfe6f83daa
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.
Great catch! I've added a use_is_mounted
hook.
addDocumentsButton: i18n.translate( | ||
'xpack.ingestPipelines.pipelineEditor.importDocumentsAccordion.importButtonLabel', | ||
{ | ||
defaultMessage: 'Import existing documents', |
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 found this phrasing a little confusing... "existing" had me trying to draw a comparison to a "non-existing" document, and "import" made me think of uploading a file. How about we use the verb "Add" instead, and instead of "existing" we clarify that the document comes from an index? I think this summarizes the help text of this section nicely.
<EuiSpacer size="l" /> | ||
|
||
{/* Documents editor */} | ||
<UseField |
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.
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 like this idea. It would require some refactoring though, since the field label is currently defined in the form schema. I'm going to leave as-is for now.
…nes/add_existing_documents
Thanks for the very helpful review @cjcenizal! I addressed your feedback via 4b4c81b. |
Sorry I'm just now getting to this. Here's some ideas for the layout:
|
@mdefazio thanks for the great suggestions! I've implemented most of them. Would you mind taking another look when you get a chance?
I like this idea. I think this work more closely aligns with #78183 though, so I will add this functionality as part of that PR.
I think this would be a nice enhancement. I'm going to put this on hold for now, but will consider adding this capability in a separate PR. |
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!
@elasticmachine merge upstream |
💚 Build SucceededHistory
To update your PR or re-run it, just comment with: |
* master: (226 commits) [Enterprise Search] Added Logic for the Credentials View (elastic#77626) [CSM] Js errors (elastic#77919) Add the @kbn/apm-config-loader package (elastic#77855) [Security Solution] Refactor useSelector (elastic#75297) Implement tagcloud renderer (elastic#77910) [APM] Alerting: Add global option to create all alert types (elastic#78151) [Ingest pipelines] Upload indexed document to test a pipeline (elastic#77939) TypeScript cleanup in visualizations plugin (elastic#78428) Lazy load metric & mardown visualizations (elastic#78391) [Detections][EQL] EQL rule execution in detection engine (elastic#77419) Update tutorial-full-experience.asciidoc (elastic#75836) Update tutorial-define-index.asciidoc (elastic#75754) Add support for runtime field types to mappings editor. (elastic#77420) [Monitoring] Usage collection (elastic#75878) [Docs][Actions] Add docs for Jira and IBM Resilient (elastic#78316) [Security Solution][Resolver] Update @timestamp formatting (elastic#78166) [Security Solution] Fix app layout (elastic#76668) [Security Solution][Resolver] 2 new functions to DAL (elastic#78477) Adds new elasticsearch client to telemetry plugin (elastic#78046) skip flaky suite (elastic#78512) (elastic#78511) (elastic#78510) (elastic#78509) (elastic#78508) (elastic#78507) (elastic#78506) (elastic#78505) (elastic#78504) (elastic#78503) (elastic#78502) (elastic#78501) (elastic#78500) ...
This PR adds the ability to upload a document(s) from an existing index when testing a pipeline.
Fixes #74796
Skipping release note as I think this change can fall under the general improvements made to the debugging workflow. Release note added on #74964.
How to review
Testing instructions
UX
I'm interested in any design feedback on this. Two questions in particular on my end:
relationships
parameter, where you could add multiple rows, but I'm not sure how useful this is.Example from mappings editor:
Screenshots
Initial view:
Accordion open:
Field errors:
API error: