-
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
[App Search] Final Document Creation API, Logic, & Summary/Error views #86822
Conversation
- errors can/should be shown in the EuiFlyoutBody banner (better UX since the JSON/file is right there for reference) vs its own page - No need to distinguish between ShowErrorSummary and ShowSuccessSummary + placeholder Summary view for now
- split up into subcomponents for easier reading/testing
@JasonStoltz Since so much code was refactored from the standalone UI, it would be awesome if you could go through and confirm/check off off the QA steps in the PR description to confirm that all behavior is still functioning as before. Please feel free to go beyond them also try and break this component as much as possible. Thanks again, I know this is a super huge PR and tried to split the commits into logical / separate pieces as much as possible (but there's still just a ton of code/tests). |
const mount = (defaults?: object) => { | ||
if (!defaults) { | ||
resetContext({}); | ||
} else { | ||
resetContext({ | ||
defaults: { | ||
enterprise_search: { | ||
app_search: { | ||
document_creation_logic: { | ||
...defaults, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
} |
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 thinking about writing a reusable test helper for this logic at some point in the future 🤔 some day!!
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.
+1
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'd be cool if it could extract the path from the logic automatically.
const mount = new Mounter(EngineLogic)
...
mount()
mount({ engineName: "foo" })
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.
Follow-up: #87247
// NOTE: I can't seem to reproduce this in a production setting. | ||
it('handles errors returned from the API', async () => { |
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 sure if I should keep this comment or not, I haven't been able to actually QA/repro this functionality (getting a summary.errors
response) in production, but our documents endpoint does seem to indicate it's there (if I'm reading the Ruby code correctly, which it's very possible I'm not).
That being said there's other scenarios that realistically wouldn't happen that we're still guarding for (such as a non-file or corrupted file being submitted) but not actively QAing for, so if that's one of those scenarios maybe I should just not leave the comment because it's not super helpful.
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 don't have an opinion here.
...earch/public/applications/app_search/components/document_creation/document_creation_logic.ts
Show resolved
Hide resolved
|
||
try { | ||
const responses = await Promise.all(promises); | ||
const summary: DocumentCreationSummary = { |
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 not a change request. I wonder if a summary
would have been better as a selector. Store the raw response
, and use a summary
selector to parse up the response into a summary. Just something to break up this monster logic file a bit. The parseSummary
could even be its own function that could be unit tested independently.
Instead of a monster summary you could even have more fine grained selectors that parse up the response as needed.
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.
Super interesting, I really like that idea! I think my only hangup there is storing response
in state, just because that feels a little weird since we're not really using it directly. I definitely agree the merging logic/parseSummary
does feel like it could be a separate utility for sure.
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.
Yeah, something to think about. State doesn't necessarily need to be used directly.
if (summary.errors.length > 0) { | ||
actions.setErrors(summary.errors); | ||
} else { | ||
actions.setSummary(summary); |
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.
A pattern to try out in the future would be to avoid individual set methods like this and instead have "success" and "failure" actions for listeners.
https://kea.js.org/docs/guide/concepts#listeners
So instead of calling setErrors
on failure, and setSummary
+ setCreationStep
on success ... call uploadDocumentsFailure
or uploadDocumentsSuccess
respectively, and handle setting errors
, summary
, and creationStep
in reducers that listen for those actions.
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'd love to have a team session (maybe a tech talk or even just a rubber ducky) about this sometime actually - I struggle a little bit about writing things "the Kea way" because it feels super unintuitive to me. Like, I get that using reducers saves us extra actions.something
calls, but to me having the action fns listed is really explicit and helpful from a developer POV as opposed to me having to ctrl+F all over the file to figure out what all vars/values an action happens to affect (vs. having them listed clearly in a row in one place).
Definitely would be super helpful for me to discuss this as a team!
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.
Yeah, its advice I've heard more than once, not only in the Kea context but also for Redux. As far as I understand it is common wisdom. It would be interesting to hear arguments against though, for sure.
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.
Ya I'd def love to see a code diff of what this logic file for example would look like. The failure/success actions make sense & are fairly straightforward, what's tripping me up I think is what to replace setWarnings
with since it feels like that goes along with setErrors
and setSummary
.
I think I'll leave this as-is for now since I'm not sure what the 'ideal' code should look like and I'd rather walk it through with someone who's got a clearer picture in their head than me, but I'm very very interested in doing that w/ someone next year!
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 think what's also extra weird for me for this specific logic file / use case is that the summary can also include "errors" (aka invalid documents) which is a failure state, so it's not 100% a success state... and it's really hard to wrap my mind around that / describe that accurately via method names other than just 'showSummary'/'setSummary'.
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 think I'm going to go ahead and merge this PR in since I have another test helper PR that depends on this work, but I'll see if I can't open up a new PR with your proposed changes as a follow-up and continue the discussion there.
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 yeah sorry, this is totally non-blocking feedback. I don't think you need to make that change now, or ever. I think there's low value in going back and rewriting things in that style. I think there can be value for new code that we write, however, which is why I opened up the conversation.
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 using that style now in a new logic file that I'm writing, so your value has been realized!! 😁
One thing that I'd also like to chat about (maybe in the PR that I'll be opening shortly) is naming for actions that follow this style. I'll admit a lot of this is habit on my part, but historically for functions I try to name them with verbSomething
(e.g., setX
, fetchY
, getZ
) instead of a name that sounds like a noun - this makes it slightly easier for me to distinguish functions from variables at a glance. That breaks a bit for this style of writing logic files, so I tried out writing story actions with an onX
prefix - e.g., onUploadDocumentsSuccess
, onFetchY
, and so forth.
Let me know what you think about that naming convention? I'm a little torn because onX
var names makes me think of jQuery and callback hells for some reason but I also think it makes sense in this 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.
Haha. I think a past tense verb would be appropriate for an action name.
uploadDocumentsSucceeded
So you're saying "what happened" in code as you write it.
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.
Hmm! My only thought is that could definitely be mistaken for a boolean at a glance - we've used past tense for boolean states in the standalone UI. Normally I prefer a isX
/hasY
/didZ
prefix/naming to indicate bools, but we're currently not consistent on that.
It also could be I'm being too prescriptive about my var/fn/bool names based on my past experiences - I've found it super helpful previously, but it's potentially not anymore since we have Typescript and Typescript should in theory be checking and documenting types for us.
...earch/public/applications/app_search/components/document_creation/document_creation_logic.ts
Show resolved
Hide resolved
...ugins/enterprise_search/public/applications/app_search/components/document_creation/utils.ts
Show resolved
Hide resolved
...rch/public/applications/app_search/components/document_creation/document_creation_flyout.tsx
Show resolved
Hide resolved
|
||
it('handles client-side errors', async () => { | ||
const { http } = HttpLogic.values; | ||
const promise = (http.post as jest.Mock).mockReturnValueOnce(new Error()); |
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 don't under stand this. How does promise
get assigned a promise value here?. You're return value that you are returning is not a promise 🤔
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 actually sure.... but for some reason the test fails / doesn't work without it :dead_inside: I think it's because of the await Promise.all(promises)
on line 187.
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 a couple of small comments but I am generally OK with this code. I tested it out, it all works well. There's great unit test coverage. I have no concerns.
60b6b81
to
0fe6f1c
Compare
@elasticmachine merge upstream |
💚 Build SucceededMetrics [docs]Module Count
Async chunks
Distributable file count
History
To update your PR or re-run it, just comment with: |
elastic#86822) * [Setup] Server API route * [Cleanup] Remove unnecessary DocumentCreationSteps - errors can/should be shown in the EuiFlyoutBody banner (better UX since the JSON/file is right there for reference) vs its own page - No need to distinguish between ShowErrorSummary and ShowSuccessSummary + placeholder Summary view for now * Add DocumentCreationLogic file upload logic * Update creation form components to show error/warning feedback * Add final post-upload summary view - split up into subcomponents for easier reading/testing * [lint] oops, double licenses * [PR feedback] map -> forEach * [PR feedback] Reset form state on flyout close Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
#86822) (#87211) * [Setup] Server API route * [Cleanup] Remove unnecessary DocumentCreationSteps - errors can/should be shown in the EuiFlyoutBody banner (better UX since the JSON/file is right there for reference) vs its own page - No need to distinguish between ShowErrorSummary and ShowSuccessSummary + placeholder Summary view for now * Add DocumentCreationLogic file upload logic * Update creation form components to show error/warning feedback * Add final post-upload summary view - split up into subcomponents for easier reading/testing * [lint] oops, double licenses * [PR feedback] map -> forEach * [PR feedback] Reset form state on flyout close Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Summary
This is the (hopefully!) final PR for the Document Creation component/flyout. It's significantly different from the standalone UI code in that it has been heavily cleaned up, modernized to use Kea logic/state, converted from custom/STUI components to EUI, and improved UI & UX wise (flyout over modal, more specific JSON errors, unifying DocumentCreationButtons, etc.).
In this final PR, users should be able to:
Screencaps
Paste text JSON upload
File upload
Invalid documents
QA
Valid documents testing
video-games.json
in our standalone repo) and confirm that it uploads without errorsLarge documents testing
Error testing
invalid
into the textarea and submit.JSON.parse: unexpected character at line 1 column 1 of the JSON data
true
,false
,null
, or1
into the textarea and submit.Document contents must be a valid JSON array or object.
{"ok": {"__proto__": "not ok"}}
into the textarea and submit.invalid
. Name ittest.json
(or anything .json)Invalid document testing
visitor
key and change the value from a number to text, e.g."visitors": "test",
{"test.key": "value"}
Checklist