Skip to content
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

Merged

Conversation

orfeas-k
Copy link
Contributor

@orfeas-k orfeas-k commented Mar 6, 2023

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.

  • Check if namespace is set through the query parameters of current URL.
  • Check if the user's previous namespace is present in the browser's local storage
  • Check in the namespaces list for a namespace with Owner role (and keep the first one)
  • Check in the namespaces list for the first valid namespace

In 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:

  • Emit an observable with it to the currentNamespace subscribers
  • Update the namespace for this specific user in the browser's localStorage
  • Update the browser's URL's query parameters with the initialized namespace

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 the single-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 WA

We 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.

Fix Cypress and Karma types conflicts resulting in VSCode errors about
expect() statements

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
@orfeas-k orfeas-k force-pushed the feature-orfeas-cdb-namespace-selector branch from 9c9f1bf to 2056634 Compare March 6, 2023 15:52
Copy link
Contributor

@tasos-ale tasos-ale left a 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.

Comment on lines 183 to 187
/**
* Receives a message from an iframe page and passes the selected namespace.
* @param {MessageEvent} event
*/
private onMessageReceived(event: any) {
Copy link
Contributor

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.

Comment on lines 70 to 71
private setCurrentNamespace(namespaces: Namespace[], user: string) {
if (user) {
Copy link
Contributor

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()?

Comment on lines 57 to 58
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
Copy link
Contributor

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

Comment on lines +117 to +122
/**
* 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.
*/
Copy link
Contributor

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 /*.

Comment on lines +20 to +29
if (res.user) {
this.user.next(res.user);
}
if (res.platform) {
this.platform.next(res.platform);
}
if (res.namespaces) {
this.namespaces.next(res.namespaces);
}
});
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Comment on lines 169 to 178
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);
}
Copy link
Contributor

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.

Copy link
Contributor

@tasos-ale tasos-ale left a 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 .

Comment on lines 13 to 16
public platform = new ReplaySubject<PlatformInfo>();
public user = new ReplaySubject<string>();
public namespaces = new ReplaySubject<Namespace[]>();
public dashboardLinks = new ReplaySubject<DashboardLinks>();
Copy link
Contributor

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.

Comment on lines 13 to 15
public namespacesSubject = new ReplaySubject<Namespace[]>();
public currentNamespaceSubject = new ReplaySubject<Namespace>();
public allNamespacesAllowedSubject = new ReplaySubject<boolean>();
Copy link
Contributor

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.

Comment on lines 42 to 57
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 => {
Copy link
Contributor

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.

Comment on lines 78 to 80
this.currentNamespaceSubject.next(this.currentNamespace);
this.updateStoragedNamespace(this.currentNamespace);
this.updateQueryParams(this.currentNamespace.namespace);
Copy link
Contributor

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.

@orfeas-k
Copy link
Contributor Author

@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!

Copy link
Contributor

@tasos-ale tasos-ale left a 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private onMessageReceived(event: any) {
private onMessageReceived(event: MessageEvent) {

});

this.ns.currentNamespaceSubject.subscribe((namespace: Namespace) => {
console.log(namespace);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log(namespace);

Comment on lines 17 to 21
namespacesSubject = this._namespacesSubject.asObservable();
currentNamespaceSubject = this._currentNamespaceSubject.asObservable();

private user: string;
private currentNamespace: Namespace | undefined;
Copy link
Contributor

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:

Suggested change
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 ?

Copy link
Contributor Author

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.

@google-oss-prow
Copy link

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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 <orfeas@arrikto.com>
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 <orfeas@arrikto.com>
Introduce and use SvgIconsService service in order to register custom
icons MatIcon directive and provide them app-wide.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
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 <orfeas@arrikto.com>
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 <orfeas@arrikto.com>
Pass the namespace query parameter (if present) to the iframe component,
using the routerLink's queryParams property.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
Post message containing the current namespace to the iframed WA. This
communication becomes possible because of library.js.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
Include subcase for getBackendErrorLog when neither error.error nor
error.log are available.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
Left sidebar menu now handles namespaced links by replacing '{ns}' with
the current namespace.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
Add UI tests with Cypress that check that the namespace selector is
initialized prioritizing correctly between options available.

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
Add unit tests for MainPageComponent's isLinkActive() and
getNamespaceParams().

Signed-off-by: Orfeas Kourkakis <orfeas@arrikto.com>
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 <orfeas@arrikto.com>
@orfeas-k orfeas-k force-pushed the feature-orfeas-cdb-namespace-selector branch from a57908d to e38f99c Compare March 24, 2023 15:47
@tasos-ale
Copy link
Contributor

/lgtm

@google-oss-prow google-oss-prow bot merged commit 0aba01d into kubeflow:master Mar 25, 2023
@tasos-ale tasos-ale deleted the feature-orfeas-cdb-namespace-selector branch March 25, 2023 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants