-
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
[Workplace Search] Add Account Settings page imported from Security plugin #99791
Conversation
3c87b4b
to
48d7bfa
Compare
This comment has been minimized.
This comment has been minimized.
48d7bfa
to
2c350dc
Compare
Thanks again for your help with this, Oleg! I have several questions:
Questions 2 and 3 are not directly related to the |
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.
The approach looks good to me. We all will probably need to sync again in the future when we decide to update how personal info screen looks like (once we have user personal settings, notifications and whatnot), but that sounds like a reasonable price for reusable components.
@legrego do see any problems with sharing ChangePassword
and PersonalInfo
components with other plugins that I'm missing? Here is some context: the personal dashboards for Workplace Search will be part of the Workplace Search Kibana plugin, or more precisely part of the a chromeless app in the Kibana plugin. Currently such dashboards contain a form to change the user password that Workplace Search users are already familiar with. Ideally we'd integrate Kibana own account management view into this flow somehow, but it doesn't seem like we have a good (architecturally and UX-wise) way of doing this now. That's how the idea to reuse only certain security UI components came up.
- Page load bundle increase for security plugin worries me a bit. Is this something we can keep at the previous level, or is the increase unavoidable?
Yeah, that worries me a bit too, but I'm not sure if we can do anything about it unless we do something like this in Security plugin:
import type { ChangePassword } from './account_management/change_password';
import type { PersonalInfo } from './account_management/personal_info';
export type { ChangePassword, PersonalInfo };
export function loadUserComponents(): Promise<{
ChangePassword: typeof ChangePassword;
PersonalInfo: typeof PersonalInfo;
}> {
return Promise.all([
import('./account_management/change_password'),
import('./account_management/personal_info'),
]).then(([comp1, comp2]) => ({
ChangePassword: comp1.ChangePassword,
PersonalInfo: comp2.PersonalInfo,
}));
}
@elastic/kibana-operations is there any recommended way to export public
components from plugin A, so that plugin B can import them, but these components aren't included into main bundle of the plugin A?
- The test errors are coming from the ui_capabilities/spaces_only test suite, but I didn't touch it. Do you have any insights into why the tests are failing?
Yeah, as you see only spaces_only
tests are failing. These tests are run with disabled security, and since you made security required dependency for EntSearch, your plugins are disabled in these tests and don't register their specific UI capabilities anymore. As I asked you in another comment, I'm not sure if it's what you really want. If it's intentional, then just remove capabilities your app registers from the test expected results.
- This is the first time I see Public APIs missing comments and Public APIs missing exports warnings. Should I run the recommended commands and commit the diff in this PR?
It's the first time I see this as well, but if running these commands produces changes that make CI happy, please do that. If not, let me know and I'll figure this out.
@@ -2,9 +2,9 @@ | |||
"id": "enterpriseSearch", | |||
"version": "kibana", | |||
"kibanaVersion": "kibana", | |||
"requiredPlugins": ["features", "licensing", "charts"], | |||
"requiredPlugins": ["features", "licensing", "charts", "security"], |
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.
question: security can potentially be disabled, are you okay with EntSearch be disabled in this case as well because of disabled required dependency?
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 haven't considered this case. I'll look into it.
(Currently, I think I'll make it an optional dependency again).
@azasypkin thanks for the ping and the context.
I'm not a fan of the increased bundle size either -- while we are technically under the We took a different approach with the Spaces plugin recently (#91976), in order to share reusable components between plugins. Essentially the components are available through an async function on the plugin's lifecycle methods, which allows us to lazy-load them, which in turn keeps them out of the page load bundle. It's a bit more work, and less elegant...but it's the best we've come up with so far. I'd suggest taking a similar approach here if we really need these components to be sharable.
I have some questions first -- they are probably silly questions, but I want to make sure I understand the problem we're trying to solve here.
|
@legrego These are great questions, thanks for asking them! Question 1
I'll copy the content from a product document that has a good explanation:
To summarize: the Workplace Search personal dashboard application hides the global Elastic header because the search user most likely doesn't know about Elastic or Kibana. The goal of this chromeless app is to hide the rest of Elastic. Hope this clarifies things a bit. Let me know if I could provide more detail. Note: We might bring the Elastic header back somewhere in 8.x+, but for 7.x the decision was to hide it completely. Question 2
Good question. I don't have a definitive answer right now. I expect that we will either:
This will require a discussion with the product team. For now not having a polished experience is expected and ok, because the Workplace Search Kibana plugin will be in beta until 8.0. Bundle size increaseThank you for the link! I'll look into loading components through an async function. |
@yakhinvadim thanks so much for the context, that's very helpful. Overall I don't have any objections to the approach you've outlined above for reusing our components, so long as we can address the async loading to keep the bundle sizes small. I'm satisfied with your answers to my first question, and I agree that we need a discussion with the product team about the second question. Ping @alexfrancoeur since we worked together recently on the profile links for cloud users 🙂. Workplace Search is adding an inlined version of Kibana's account settings page within their personal dashboard view. This will work well for users in the |
The only real option is to export an async function from the root of plugin A and then use a We could probably add the ability to specify an The holy grail would be to allow standard |
Thanks for the ping @legrego! For the time being, until we have a better user model for the Cloud / Stack, I'm wondering if we should be providing a similar experience that we do for Kibana. Do we have a sense of how many users will be coming from Cloud SSO? @jonasll @yakhinvadim Let me know if it'd be helpful to connect for 15 minutes and talk through what that experience is today. |
@alexfrancoeur It would be helpful. To answer the question: I expect few users (not admins) to come from Cloud SSO or native. We expect most to come from organizational SSO experiences powered by AD, Okta and the likes. |
These components are needed to enable async loading of Security components into Enterprise Search. The components are copied without any changes except for i18n ids, so it's easier to DRY out in the future if needed.
The patterns were mostly copied from Spaces plugin
use them in the new AccountSettings component
2c350dc
to
efa7d68
Compare
@azasypkin This should be ready for re-review. The first 4 commits in this PR only touch Security plugin files. Last 4 - only Enterprise Search plugin files. I will request a review from my team for Enterprise Search changes after you approve the changes in the Security plugin. Major changes since last time
Implementation details
ScreencastThrottled to Fast 3G. Can be skipped to 0:15. Screen.Cast.2021-06-02.at.11.43.11.AM.mp4 |
ACK: will review on Monday, Jun 7th at the latest. |
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.
Looks good, just left a few suggestions. I know you tried to replicate the lazy-loading code from the Spaces as much as possible, but I believe there is no reason to reproduce all the complexity we needed there, but may never need here.
What do you think?
@@ -97,6 +100,10 @@ export class SecurityPlugin | |||
logoutUrl, | |||
}); | |||
|
|||
this.uiApi = getUiApi({ |
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.
note: It looks like we don't use uiApi
in the setup
yet, would you mind just calling getUiApi
in the start
instead? This way we don't need to persist it anywhere.
Also since we'll call it in the start
, we can pass core: CoreStart
directly instead of getStartServices
.
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 suggestion! Updated here: 4f8f832
|
||
import type { StartServicesAccessor } from 'src/core/public'; | ||
|
||
import type { ChangePasswordProps } from '../account_management/change_password'; |
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: would you mind exporting these props types from '../account_management/index.ts
so that we can import them both from '../account_management'
directly?
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.
Update here: ebc2753. Could you confirm that this is what we want? I'm still a bit confused about what should be exported from components and what from index files.
export interface UiApi { | ||
components: { | ||
getPersonalInfo: LazyComponentFn<PersonalInfoProps>; | ||
getChangePassword: LazyComponentFn<ChangePasswordProps>; | ||
}; | ||
UserAPIClient: typeof UserAPIClient; | ||
} |
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.
suggestion: Since we switched to returning components from the start
contract, it seems we don't need to export UserAPIClient
anymore. We have everything to be able to instantiate it internally, see my comment in change_password_async.tsx
.
export interface UiApi { | |
components: { | |
getPersonalInfo: LazyComponentFn<PersonalInfoProps>; | |
getChangePassword: LazyComponentFn<ChangePasswordProps>; | |
}; | |
UserAPIClient: typeof UserAPIClient; | |
} | |
export interface UiApi { | |
components: { | |
getPersonalInfo: LazyComponentFn<PersonalInfoProps>; | |
getChangePassword: LazyComponentFn<Pick<ChangePasswordProps, 'user'>>; | |
}; | |
} |
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.
Removed it here: 706ac2e
export const getChangePasswordComponent = async (): Promise<React.FC<ChangePasswordProps>> => { | ||
const { ChangePassword } = await import('./change_password'); | ||
return (props: ChangePasswordProps) => { | ||
return <ChangePassword {...props} />; | ||
}; | ||
}; |
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: LazyWrapper
has access to the CoreStart
already, so we should be able to leverage it here. This would simplify API for our consumers since they'll only need to provide user
and won't need to care about internal implementtion details like notifications
and userAPIClient
.
export const getChangePasswordComponent = async (): Promise<React.FC<ChangePasswordProps>> => { | |
const { ChangePassword } = await import('./change_password'); | |
return (props: ChangePasswordProps) => { | |
return <ChangePassword {...props} />; | |
}; | |
}; | |
export const getChangePasswordComponent = async ( | |
core: CoreStart | |
): Promise<React.FC<Pick<ChangePasswordProps, 'user'>>> => { | |
const { ChangePassword } = await import('./change_password'); | |
return (props: Pick<ChangePasswordProps, 'user'>) => { | |
return ( | |
<ChangePassword | |
notifications={core.notifications} | |
userAPIClient={new UserAPIClient(core.http)} | |
{...props} | |
/> | |
); | |
}; | |
}; |
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.
Was really happy it worked! See this commit: 9f18ea9
Thanks for the review, @azasypkin! |
... `notifications` and `userAPIClient` props in the security plugin
It's not needed anymore since the components are initiated with this prop already passed
…ent/index.ts This makes it easier to import these props from outside the account_management folder
It is not needed anymore since we're initializing security components with notifications already provided
@azasypkin This should be ready for re-review! |
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, just few more nits, thanks 👍
|
||
export const getChangePasswordComponent = async ( | ||
core: CoreStart | ||
): Promise<React.FC<Pick<ChangePasswordProps, 'user'>>> => { |
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: I learnt yesterday that Pick
doesn't play well with our dev docs. Would you mind creating two separate interfaces inside of change_password
instead and replacing any Pick
-based types with dedicated interface?
export interface ChangePasswordProps {
user: AuthenticatedUser;
}
export interface ChangePasswordPropsInternal extends ChangePasswordProps {
userAPIClient: PublicMethodsOf<UserAPIClient>;
notifications: NotificationsSetup;
}
(and you also need to merge the latest master and likely to update latest version of SecurityPluginStart
)
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, good to know, and thanks for the link! I replaced it here: 2425821
export type { ChangePasswordProps } from './change_password/change_password'; | ||
export type { PersonalInfoProps } from './personal_info/personal_info'; |
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: please export these types from ./change_password | personal_info/index.ts
. We use index.ts
as a grouping mechanism for all exports where feasible.
export type { ChangePasswordProps } from './change_password/change_password'; | |
export type { PersonalInfoProps } from './personal_info/personal_info'; | |
export type { ChangePasswordProps } from './change_password'; | |
export type { PersonalInfoProps } from './personal_info'; |
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.
Got it, thanks! Updated here: 9b932dc
export interface UiApi { | ||
components: { | ||
getPersonalInfo: LazyComponentFn<PersonalInfoProps>; | ||
getChangePassword: LazyComponentFn<Pick<ChangePasswordProps, 'user'>>; |
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: (once you split this one in two interfaces)
getChangePassword: LazyComponentFn<Pick<ChangePasswordProps, 'user'>>; | |
getChangePassword: LazyComponentFn<ChangePasswordProps>; |
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 here: 2425821
import { getChangePasswordComponent } from '../account_management/change_password/change_password_async'; | ||
import { getPersonalInfoComponent } from '../account_management/personal_info/personal_info_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.
nit: (once you propagate all required exports through relevant index.ts
files)
import { getChangePasswordComponent } from '../account_management/change_password/change_password_async'; | |
import { getPersonalInfoComponent } from '../account_management/personal_info/personal_info_async'; | |
import { getChangePasswordComponent, getPersonalInfoComponent } from '../account_management'; |
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 tried to did it before, but it resulted in an increased bundle size.
I took the following info from security/target/public/metrics.json
after running the first command from CI Metrics doc.
Before:
{
"group": "page load bundle size",
"id": "security",
"value": 87166,
"limit": 95864,
"limitConfigPath": "packages/kbn-optimizer/limits.yml"
}
After:
{
"group": "page load bundle size",
"id": "security",
"value": 101392,
"limit": 95864,
"limitConfigPath": "packages/kbn-optimizer/limits.yml"
}
I'm guessing that once async components are exported through the index file and we use that index file somewhere that also brings all the sync components' dependencies and increases bundle size.
I kept the imports as is, but let me know if you have more thoughts.
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, okay, let's keep it as is then. Can you please leave a brief comment for these imports so that readers can quickly understand why it's done this way?
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 idea, absolutely.
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.
Added a comment here: 1880755
const [currentUser, setCurrentUser] = useState<AuthenticatedUser | null>(null); | ||
|
||
useEffect(() => { | ||
security!.authc!.getCurrentUser().then(setCurrentUser); |
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.
question: won't we have an unhandled exception here in case security plugin is disabled?
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 checking our plugin code as well! Good question.
No, if this component loads, we are guaranteed to have the security plugin enabled.
One of the requirements for being able to run Workplace Search is to have security enabled (see Step 2 in our installation docs). If security is disabled, Workplace Search won't run and this page could not be opened.
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.
Got it, thanks for clarifying! Was just confused by these !
and was wondering if security
and its properties can really be undefined
or not (by the way it seems !
isn't strictly needed for security
itself and is only needed to access its properties).
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.
You're right. I guess I overdid it here. 😄
I plan to do a follow-up PR with some cleanup in the enterprise_search plugin only.
I'll update this line there if you don't mind.
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.
Sounds good to me 👍
💚 Build SucceededMetrics [docs]Module Count
Async chunks
Public APIs missing exports
Page load bundle
Unknown metric groupsAPI count
async chunk count
History
To update your PR or re-run it, just comment with: |
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 great work Vadim! Thanks for knocking it out 👍
Thanks to everyone and especially @azasypkin for helping with this! 🙌 |
…lugin (elastic#99791) * Copy lazy_wrapper and suspense_error_boundary from Spaces plugin These components are needed to enable async loading of Security components into Enterprise Search. The components are copied without any changes except for i18n ids, so it's easier to DRY out in the future if needed. * Create async versions of personal_info and change_password components * Create ui_api that allows to load Security components asuncronously The patterns were mostly copied from Spaces plugin * Make ui_api available through Security components's lifecycle methods * Import Security plugin into Enterprise Search * Add Security plugin and Notifications service to Kibana Logic file * Export the required components from the Security plugin and use them in the new AccountSettings component * Update link to the Account Settings page * Move getUiApi call to security start and pass core instead of getStartServices * Simplify import of change_password_async component by providing... ... `notifications` and `userAPIClient` props in the security plugin * Remove UserAPIClient from ui_api It's not needed anymore since the components are initiated with this prop already passed * Export ChangePasswordProps and PersonalInfoProps from account_management/index.ts This makes it easier to import these props from outside the account_management folder * Remove notifications service from kibana_logic It is not needed anymore since we're initializing security components with notifications already provided * Add UiApi to SecurityPluginStart interface * Utilize index files for exporting Props types * Replace Pick<...> with two separate interfaces as it doesn't work well with our docs * Add a comment explaining why we're not loading async components through index file
💚 Backport successful
This backport PR will be merged automatically after passing CI. |
…lugin (#99791) (#101930) * Copy lazy_wrapper and suspense_error_boundary from Spaces plugin These components are needed to enable async loading of Security components into Enterprise Search. The components are copied without any changes except for i18n ids, so it's easier to DRY out in the future if needed. * Create async versions of personal_info and change_password components * Create ui_api that allows to load Security components asuncronously The patterns were mostly copied from Spaces plugin * Make ui_api available through Security components's lifecycle methods * Import Security plugin into Enterprise Search * Add Security plugin and Notifications service to Kibana Logic file * Export the required components from the Security plugin and use them in the new AccountSettings component * Update link to the Account Settings page * Move getUiApi call to security start and pass core instead of getStartServices * Simplify import of change_password_async component by providing... ... `notifications` and `userAPIClient` props in the security plugin * Remove UserAPIClient from ui_api It's not needed anymore since the components are initiated with this prop already passed * Export ChangePasswordProps and PersonalInfoProps from account_management/index.ts This makes it easier to import these props from outside the account_management folder * Remove notifications service from kibana_logic It is not needed anymore since we're initializing security components with notifications already provided * Add UiApi to SecurityPluginStart interface * Utilize index files for exporting Props types * Replace Pick<...> with two separate interfaces as it doesn't work well with our docs * Add a comment explaining why we're not loading async components through index file Co-authored-by: Vadim Yakhin <[email protected]>
This description is outdated, skip to #99791 (comment) for latest update.
Summary
This PR adds the Account Settings page to Workplace Search personal dashboard. The components for this page were imported from the Security plugin.
@azasypkin
I'm looking for feedback on the general approach. Please let me know what you think and if you see any areas for improvement. Commits 632589f and 3a17681 should be most relevant to you.
I will request a review from my team if the general approach looks good to you.
Screenshots
Workplace Search Account Settings page:
Screen.Cast.2021-05-11.at.12.29.38.PM.mp4
Current Account page in Security plugin for the reference:
Checklist