-
Notifications
You must be signed in to change notification settings - Fork 2.5k
cdb(front): Add namespace selector #7030
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
cdb(front): Add namespace selector #7030
Conversation
Fix Cypress and Karma types conflicts resulting in VSCode errors about expect() statements Signed-off-by: Orfeas Kourkakis <[email protected]>
9c9f1bf
to
2056634
Compare
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.
@orfeas-k Thanks for your PR. Please take a look at my comments.
/** | ||
* Receives a message from an iframe page and passes the selected namespace. | ||
* @param {MessageEvent} event | ||
*/ | ||
private onMessageReceived(event: 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.
Since we are using TS we don't need JSDoc types, so we can replace the any
type here. I don't have a strong opinion about the description, we can keep it even if this method is private.
private setCurrentNamespace(namespaces: Namespace[], user: string) { | ||
if (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.
Is it possible to not have the user
when we use the setCurrentNamespace()
?
this.router.events.subscribe(event => { | ||
if (event instanceof NavigationEnd) { |
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.
We can use a filter()
and remove the if statement
since we only care for NavigationEnd
/** | ||
* Append current namespace to query parameters before using router.navigate | ||
* since iframe's internal URL doesn't hold any information regarding the | ||
* namespace and we want to prevent it from discarding this infromation from | ||
* the Browser's URL. | ||
*/ |
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.
FYI https://google.github.io/styleguide/jsguide.html#formatting-block-comment-style. There is no need to use /**
, you can change it to /*
.
if (res.user) { | ||
this.user.next(res.user); | ||
} | ||
if (res.platform) { | ||
this.platform.next(res.platform); | ||
} | ||
if (res.namespaces) { | ||
this.namespaces.next(res.namespaces); | ||
} | ||
}); |
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.
Do we really need 3 different subjects for these values?
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.
We don't really need 3 different subjects since we fetch all of the above simultaneously from the same request. I followed the above approach because I found it to be cleaner since some components may need only one of these properties. However, I 'm open to reconsidering it, since this results in some other components having to subscribe more than once.
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 a strong opinion either. So, let's not change it.
private updateStoragedNamespace(namespace: Namespace | undefined) { | ||
// Save the user's choice so we are able to restore it, | ||
// when re-loading the page without a queryParam | ||
const localStorageKey = | ||
'selectedNamespace/' + ((this.user && '.' + this.user) || ''); | ||
this.localStorage.set(localStorageKey, namespace?.namespace); | ||
} |
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.
In general, try to pass everything as a parameter. It will make testing easier.
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 few more comments @orfeas-k .
public platform = new ReplaySubject<PlatformInfo>(); | ||
public user = new ReplaySubject<string>(); | ||
public namespaces = new ReplaySubject<Namespace[]>(); | ||
public dashboardLinks = new ReplaySubject<DashboardLinks>(); |
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.
Let's add 1
as the buffer size here.
public namespacesSubject = new ReplaySubject<Namespace[]>(); | ||
public currentNamespaceSubject = new ReplaySubject<Namespace>(); | ||
public allNamespacesAllowedSubject = new ReplaySubject<boolean>(); |
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 with buffer size.
this.env.user.subscribe((user: string) => { | ||
this.user = user; | ||
}); | ||
|
||
this.env.namespaces.subscribe((namespaces: Namespace[]) => { | ||
namespaces = [this.allNamespacesOption, ...namespaces]; | ||
this.namespaces = namespaces; | ||
this.namespacesSubject.next(namespaces); | ||
|
||
this.allNamespacesAllowed = this.calcAllNamespacesOption(this.router.url); | ||
this.allNamespacesAllowedSubject.next(this.allNamespacesAllowed); | ||
|
||
this.setCurrentNamespace(namespaces, this.user); | ||
}); | ||
|
||
this.router.events.subscribe(event => { |
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.
In order to avoid having 3 different subscriptions we can subscribe to events
first and then switch to user
and namespaces
.
this.currentNamespaceSubject.next(this.currentNamespace); | ||
this.updateStoragedNamespace(this.currentNamespace); | ||
this.updateQueryParams(this.currentNamespace.namespace); |
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.
We could probably reuse selectNamespace
here with a few changes.
@tasos-ale Thank you for your comments. I pushed commits that address them. This includes as well a rewrite of the namespace service core functionality, as well as tests for different parts of this PR. Let me know what you think! |
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.
@orfeas-k a few minor comments.
} | ||
|
||
// Receives a message from an iframe page and passes the selected namespace. | ||
private onMessageReceived(event: 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.
private onMessageReceived(event: any) { | |
private onMessageReceived(event: MessageEvent) { |
}); | ||
|
||
this.ns.currentNamespaceSubject.subscribe((namespace: Namespace) => { | ||
console.log(namespace); |
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.
console.log(namespace); |
namespacesSubject = this._namespacesSubject.asObservable(); | ||
currentNamespaceSubject = this._currentNamespaceSubject.asObservable(); | ||
|
||
private user: string; | ||
private currentNamespace: Namespace | undefined; |
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.
Maybe we can do this:
namespacesSubject = this._namespacesSubject.asObservable(); | |
currentNamespaceSubject = this._currentNamespaceSubject.asObservable(); | |
private user: string; | |
private currentNamespace: Namespace | undefined; | |
namespaces = this._namespacesSubject.asObservable(); | |
currentNamespace = this._currentNamespaceSubject.asObservable(); | |
private _user: string; | |
private _currentNamespace: Namespace | undefined; |
wdyt @orfeas-k ?
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.
Sure, but I 'll rename them from _variable
to prvVariable
because that's what we do the in the rest of KF WAs as well.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: tasos-ale The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Introduce EnvironmentService that will be responsible for storing information coming from the backend service and that are related to the environment. Signed-off-by: Orfeas Kourkakis <[email protected]>
Introduce LocalStorageService in order to wrap the native localStorage API and use the same prefix for items used by CDB. Signed-off-by: Orfeas Kourkakis <[email protected]>
Introduce and use SvgIconsService service in order to register custom icons MatIcon directive and provide them app-wide. Signed-off-by: Orfeas Kourkakis <[email protected]>
Introduce CDBNamespaceService that will be responsible managing the namespace for the whole app. Any component that may need to access/change the namespace being used, will have to use this service. More specifically, it will: - Hold information about namespace and emit observables when this changes. - Inform components about available namespaces. - Initialize the namespace when it receives the available namespaces. - Modify the namespace's value. - Decide if All namespaces option is allowed at all times. - Provide components with namespace related constants. Signed-off-by: Orfeas Kourkakis <[email protected]>
Introduce and use NamespaceSelector component in the top toolbar which allows the user to view and modify the selected namespace. Signed-off-by: Orfeas Kourkakis <[email protected]>
Pass the namespace query parameter (if present) to the iframe component, using the routerLink's queryParams property. Signed-off-by: Orfeas Kourkakis <[email protected]>
Post message containing the current namespace to the iframed WA. This communication becomes possible because of library.js. Signed-off-by: Orfeas Kourkakis <[email protected]>
Include subcase for getBackendErrorLog when neither error.error nor error.log are available. Signed-off-by: Orfeas Kourkakis <[email protected]>
Left sidebar menu now handles namespaced links by replacing '{ns}' with the current namespace. Signed-off-by: Orfeas Kourkakis <[email protected]>
Add UI tests with Cypress that check that the namespace selector is initialized prioritizing correctly between options available. Signed-off-by: Orfeas Kourkakis <[email protected]>
Add unit tests for MainPageComponent's isLinkActive() and getNamespaceParams(). Signed-off-by: Orfeas Kourkakis <[email protected]>
Add unit tests for CDB's namespace service to check that it: - validates a namespace as expected - updates query parameters with namespace Signed-off-by: Orfeas Kourkakis <[email protected]>
a57908d
to
e38f99c
Compare
/lgtm |
* cdb(front): Fix cypress and Karma types conflict Fix Cypress and Karma types conflicts resulting in VSCode errors about expect() statements Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Introduce EnvironmentService Introduce EnvironmentService that will be responsible for storing information coming from the backend service and that are related to the environment. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Introduce LocalStorageService Introduce LocalStorageService in order to wrap the native localStorage API and use the same prefix for items used by CDB. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Introduce SvgIconsService Introduce and use SvgIconsService service in order to register custom icons MatIcon directive and provide them app-wide. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Introduce CDBNamespaceService Introduce CDBNamespaceService that will be responsible managing the namespace for the whole app. Any component that may need to access/change the namespace being used, will have to use this service. More specifically, it will: - Hold information about namespace and emit observables when this changes. - Inform components about available namespaces. - Initialize the namespace when it receives the available namespaces. - Modify the namespace's value. - Decide if All namespaces option is allowed at all times. - Provide components with namespace related constants. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Introduce NamespaceSelector component Introduce and use NamespaceSelector component in the top toolbar which allows the user to view and modify the selected namespace. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Pass query parameters to iframe Pass the namespace query parameter (if present) to the iframe component, using the routerLink's queryParams property. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Post message with namespace to iframed WA Post message containing the current namespace to the iframed WA. This communication becomes possible because of library.js. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Improve ErrorInterceptor getBackendError Include subcase for getBackendErrorLog when neither error.error nor error.log are available. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Handle namespaced menu items Left sidebar menu now handles namespaced links by replacing '{ns}' with the current namespace. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Add UI tests for namespace selector Add UI tests with Cypress that check that the namespace selector is initialized prioritizing correctly between options available. Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Add unit tests for MainPageComponent Add unit tests for MainPageComponent's isLinkActive() and getNamespaceParams(). Signed-off-by: Orfeas Kourkakis <[email protected]> * cdb(front): Add unit tests for CDBNamespaceService Add unit tests for CDB's namespace service to check that it: - validates a namespace as expected - updates query parameters with namespace Signed-off-by: Orfeas Kourkakis <[email protected]> --------- Signed-off-by: Orfeas Kourkakis <[email protected]>
This PR is part of the Rewrite the Central Dashboard in Angular effort and comes right after this one.
Namespace Service
Responsible for handling all actions related to the namespace in CDB (keeping information about the current namespace, retrieving most recent namespace used, updating components about namespace change etc) will be the new NamespaceService. Any component that needs to interact with the app's namespace will use this service.
Initializing the namespace
Once we receive the namespaces available, we need to initialize the selected namespace. We follow the below steps in order to decide upon the namespace.
query parameters
of current URL.local storage
Owner
role (and keep the first one)valid
namespaceIn every of these steps, if we find a namespace, we return it only if this namespace option is also valid (considering also the
All namespaces
option).After we finalize on the namespace, we also:
currentNamespace
subscriberslocalStorage
Select namespace
The last three steps also take place when a user changes the selected namespace via the Namespace selector dropdown.
Single user mode
In the early days, there was the notion of running Kubeflow for a single user. In that case there was no header in the requests with the user info. Extending what we decided here for
isolationMode
variable (at that point we did it in regard to Kubeflow's Version), we decide to drop thesingle-user
mode from CDB. Thus, we will also drop requests at/api/workgroup/exists
endpoint, from where we received until now user related information.Based though on that information, we also enabled/disabled the Registration page and modified the content of the Manage contributors page.
Manage contributors page
We want admins to manage users in a GitOps way, and not via that UI.
Registration page
We also had the Registration page where the user can create a user Profile (which creates a namespace).
For the moment, we move forward without implementing these two pages in the new Central Dashboard.
Post
PARENT_CONNECTED_EVENT
to iframed WAWe decided to not post a message of type
PARENT_CONNECTED_EVENT
as we did in the old dashboard (we will only post a message with the selected namespace). We don't see any use of it currently and thus, we will not include it in CDB.