Skip to content

Commit

Permalink
merge rmf-auth into dashboard (#985)
Browse files Browse the repository at this point in the history
Signed-off-by: Teo Koon Peng <teokoonpeng@gmail.com>
  • Loading branch information
koonpeng authored Aug 5, 2024
1 parent 9a250d4 commit b4c2b06
Show file tree
Hide file tree
Showing 39 changed files with 207 additions and 1,765 deletions.
1 change: 0 additions & 1 deletion .github/workflows/dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on:
- '.github/workflows/dashboard.yml'
- 'packages/dashboard/**'
- 'packages/react-components/**'
- 'packages/rmf-auth/**'
- 'packages/rmf-models/**'
- 'packages/api-client/**'
push:
Expand Down
41 changes: 0 additions & 41 deletions .github/workflows/rmf-auth.yml

This file was deleted.

41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Nightly](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml) [![Dashboard End-to-End](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml) [![react-components](https://github.com/open-rmf/rmf-web/workflows/react-components/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Areact-components+branch%3Amain) [![dashboard](https://github.com/open-rmf/rmf-web/workflows/dashboard/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Adashboard+branch%3Amain) [![api-server](https://github.com/open-rmf/rmf-web/workflows/api-server/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Aapi-server+branch%3Amain) [![rmf-auth](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml) [![ros-translator](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml) [![api-client](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml) [![codecov](https://codecov.io/gh/open-rmf/rmf-web/branch/main/graph/badge.svg)](https://codecov.io/gh/open-rmf/rmf-web)
[![Nightly](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml) [![Dashboard End-to-End](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml) [![react-components](https://github.com/open-rmf/rmf-web/workflows/react-components/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Areact-components+branch%3Amain) [![dashboard](https://github.com/open-rmf/rmf-web/workflows/dashboard/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Adashboard+branch%3Amain) [![api-server](https://github.com/open-rmf/rmf-web/workflows/api-server/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Aapi-server+branch%3Amain) [![ros-translator](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml) [![api-client](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml) [![codecov](https://codecov.io/gh/open-rmf/rmf-web/branch/main/graph/badge.svg)](https://codecov.io/gh/open-rmf/rmf-web)

# RMF Web

Expand All @@ -21,12 +21,14 @@ Open-RMF Web is a collection of packages that provide a web-based interface for
We currently support [Ubuntu 24.04](https://releases.ubuntu.com/noble/), [ROS 2 Jazzy](https://docs.ros.org/en/jazzy/index.html) and Open-RMF's [22.09](https://github.com/open-rmf/rmf/releases/tag/22.09) release. Other distributions may work as well, but is not guaranteed.

Install pnpm and nodejs

```bash
curl -fsSL https://get.pnpm.io/install.sh | bash -
pnpm env use --global lts
```

For Debian/Ubuntu systems,

```bash
sudo apt install python3-pip python3-venv
```
Expand All @@ -35,26 +37,29 @@ sudo apt install python3-pip python3-venv

Refer to the following documentation for either building from source or installing released binaries:

* [rmf](https://github.com/open-rmf/rmf)
- [rmf](https://github.com/open-rmf/rmf)

> **Note**
> Simulation demos are not part of the released binaries, and therefore a built workspace with at least the [demos repository](https://github.com/open-rmf/rmf_demos) would be required for trying out the web dashboard with simulation.
### Install dependencies

Run

```bash
pnpm install
```

You may also install dependencies for only a subset of the packages

```bash
pnpm install -w --filter <package>...
```

### Launching

Source Open-RMF and launch the dashboard in development mode,

```bash
# For binary installation
source /opt/ros/jazzy/setup.bash
Expand Down Expand Up @@ -117,27 +122,27 @@ pnpm run start

# Contribution guide

* For general contribution guidelines, see [CONTRIBUTING](CONTRIBUTING.md).
* Follow [typescript guidelines](https://basarat.gitbook.io/typescript/styleguide).
* When introducing a new feature or component in [`react-components`](packages/react-components), write tests and stories.
* When introducing a new feature in [`dashboard`](packages/dashboard), write tests as well as [e2e](packages/dashboard-e2e) test whenever possible.
* When introducing API changes with [`api-server`](packages/api-server),
* If the new changes are to be used externally (outside of the web packages, with other Open-RMF packages for example), make changes to [`rmf_api_msgs`](https://github.com/open-rmf/rmf_api_msgs), before generating the required models using [this script](packages/api-server/generate-models.sh) with modified commit hashes.
* Don't forget to update the API client with the newly added changes with [these instructions](packages/api-client/README.md/#generating-rest-api-client).
* Check out the latest API definitions [here](https://open-rmf.github.io/rmf-web/), or visit `/docs` relative to your running server's url, e.g. `http://localhost:8000/docs`.
* Develop the frontend without launching any Open-RMF components using [storybook](packages/dashboard/README.md/#storybook).
* For integration with new devices/infrastructure, check out [Robot Interaction Objects (RIO)](https://github.com/open-rmf/rmf-web/wiki/Robot-Interaction-Objects-(RIO)).
* Update documentation alongside development, and update the [`ros2multirobotbook`](https://osrf.github.io/ros2multirobotbook) where necessary.
- For general contribution guidelines, see [CONTRIBUTING](CONTRIBUTING.md).
- Follow [typescript guidelines](https://basarat.gitbook.io/typescript/styleguide).
- When introducing a new feature or component in [`react-components`](packages/react-components), write tests and stories.
- When introducing a new feature in [`dashboard`](packages/dashboard), write tests as well as [e2e](packages/dashboard-e2e) test whenever possible.
- When introducing API changes with [`api-server`](packages/api-server),
- If the new changes are to be used externally (outside of the web packages, with other Open-RMF packages for example), make changes to [`rmf_api_msgs`](https://github.com/open-rmf/rmf_api_msgs), before generating the required models using [this script](packages/api-server/generate-models.sh) with modified commit hashes.
- Don't forget to update the API client with the newly added changes with [these instructions](packages/api-client/README.md/#generating-rest-api-client).
- Check out the latest API definitions [here](https://open-rmf.github.io/rmf-web/), or visit `/docs` relative to your running server's url, e.g. `http://localhost:8000/docs`.
- Develop the frontend without launching any Open-RMF components using [storybook](packages/dashboard/README.md/#storybook).
- For integration with new devices/infrastructure, check out [Robot Interaction Objects (RIO)](<https://github.com/open-rmf/rmf-web/wiki/Robot-Interaction-Objects-(RIO)>).
- Update documentation alongside development, and update the [`ros2multirobotbook`](https://osrf.github.io/ros2multirobotbook) where necessary.

# Configuration

* See the [rmf-dashboard](packages/dashboard/README.md#configuration) docs for the frontend build-time and run-time configurations.
* See the [api-server](packages/api-server/README.md#configuration) docs for API server run-time configurations.
- See the [rmf-dashboard](packages/dashboard/README.md#configuration) docs for the frontend build-time and run-time configurations.
- See the [api-server](packages/api-server/README.md#configuration) docs for API server run-time configurations.

# Troubleshooting

* If a feature is missing or is not working, it could be only available in an Open-RMF source build, and not in the binaries. Try building Open-RMF from source and source that new workspace before launching the API server. `rmf-web` may use in-development features of Open-RMF.
- If a feature is missing or is not working, it could be only available in an Open-RMF source build, and not in the binaries. Try building Open-RMF from source and source that new workspace before launching the API server. `rmf-web` may use in-development features of Open-RMF.

* Creating tasks from the web dashboard when running a simulated Open-RMF deployment will require the task start time suit simulation time, which starts from unix millis 0. Try creating the same task with a start date of before the year of 1970.
- Creating tasks from the web dashboard when running a simulated Open-RMF deployment will require the task start time suit simulation time, which starts from unix millis 0. Try creating the same task with a start date of before the year of 1970.

* Check if the issue has already been [reported or fixed](https://github.com/open-rmf/rmf-web/issues).
- Check if the issue has already been [reported or fixed](https://github.com/open-rmf/rmf-web/issues).
4 changes: 0 additions & 4 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,3 @@ flags:
paths:
- packages/api-server
carryforward: true
rmf-auth:
paths:
- packages/rmf-auth
carryforward: true
4 changes: 3 additions & 1 deletion packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
"debug": "^4.2.0",
"eventemitter3": "^4.0.7",
"jsdom": "^24.1.1",
"keycloak-js": "^25.0.2",
"react": "^18.2.0",
"react-components": "workspace:*",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.13",
"react-grid-layout": "^1.3.4",
"react-router": "^6.14.1",
"react-router-dom": "^6.14.1",
"rmf-auth": "workspace:*",
"rmf-models": "workspace:*",
"rxjs": "^7.5.5",
"three": "^0.166.1"
Expand All @@ -66,11 +66,13 @@
"@testing-library/dom": "^9.3.4",
"@testing-library/react": "^14.2.2",
"@testing-library/user-event": "^14.5.2",
"@types/history": "^5.0.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"@vitest/coverage-v8": "^2.0.4",
"api-server": "file:../api-server",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"history": "^5.3.0",
"storybook": "^8.0.5",
"typescript": "~5.4.3",
"vite": "^5.3.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/app-config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { getDefaultTaskDefinition, TaskDefinition } from 'react-components';
import { Authenticator, KeycloakAuthenticator, StubAuthenticator } from 'rmf-auth';

import appConfigJson from '../app-config.json';
import { Authenticator, KeycloakAuthenticator, StubAuthenticator } from './auth';
import { BasePath } from './util/url';

export interface RobotResource {
Expand Down
File renamed without changes.
7 changes: 7 additions & 0 deletions packages/dashboard/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './authenticator';
export * from './keycloak';
export * from './login-card';
export * from './login-page';
export * from './private-route';
export * from './stub';
export * from './user-profile';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Debug from 'debug';
import EventEmitter from 'eventemitter3';
import Keycloak, { KeycloakInstance } from 'keycloak-js';
import Keycloak from 'keycloak-js';

import { Authenticator, AuthenticatorEventType } from './authenticator';

Expand Down Expand Up @@ -31,7 +31,7 @@ export class KeycloakAuthenticator
*/
constructor(config: Keycloak.KeycloakConfig | string, silentCheckSsoRedirectUri?: string) {
super();
this._inst = Keycloak(config);
this._inst = new Keycloak(config);
this._silentCheckSsoRedirectUri = silentCheckSsoRedirectUri;
}

Expand Down Expand Up @@ -118,7 +118,7 @@ export class KeycloakAuthenticator
}

private _initialized = false;
private _inst: KeycloakInstance;
private _inst: Keycloak;
private _silentCheckSsoRedirectUri?: string;
private _user?: string;
private _isAdmin = false;
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render } from '@testing-library/react';
import React from 'react';
import { it } from 'vitest';

import { LoginPage } from './login-page';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { styled } from '@mui/material';
import React from 'react';

import { LoginCard, LoginCardProps } from './login-card';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render } from '@testing-library/react';
import { createMemoryHistory, MemoryHistory } from 'history';
import React from 'react';
import { Router } from 'react-router-dom';
import { beforeEach, describe, expect, it } from 'vitest';

import { PrivateRoute } from './private-route';

Expand All @@ -13,7 +13,7 @@ describe('PrivateRoute', () => {
history.push('/private');
});

test('renders unauthorizedComponent when unauthenticated', () => {
it('renders unauthorizedComponent when unauthenticated', () => {
const root = render(
<Router location={history.location} navigator={history}>
<PrivateRoute path="/private" unauthorizedComponent="test" user={null} />
Expand All @@ -22,7 +22,7 @@ describe('PrivateRoute', () => {
expect(() => root.getByText('test')).not.toThrow();
});

test('renders children when authenticated', () => {
it('renders children when authenticated', () => {
const root = render(
<Router location={history.location} navigator={history}>
<PrivateRoute path="/private" user="test" unauthorizedComponent="unauthorized">
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* istanbul ignore file */

import { Configuration, DefaultApi, Permission, User } from 'api-client';
import React from 'react';

import Authenticator from '../authenticator';
import Authenticator from './authenticator';

export interface UserProfile {
user: User;
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import './app.css';

import React from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import { LoginPage, PrivateRoute } from 'rmf-auth';

import { AppConfigContext, AuthenticatorContext, ResourcesContext } from '../app-config';
import { LoginPage, PrivateRoute } from '../auth';
import {
AdminRoute,
CustomRoute1,
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/appbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import {
NavigationBar,
} from 'react-components';
import { useLocation, useNavigate } from 'react-router-dom';
import { UserProfileContext } from 'rmf-auth';
import { Subscription } from 'rxjs';

import {
Expand All @@ -58,6 +57,7 @@ import {
AuthenticatorContext,
ResourcesContext,
} from '../app-config';
import { UserProfileContext } from '../auth';
import { useCreateTaskFormData } from '../hooks/useCreateTaskForm';
import useGetUsername from '../hooks/useFetchUser';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// import { Task } from 'api-client';
import { UserProfile } from 'rmf-auth';
import { UserProfile } from '../auth';

export enum RmfAction {
TaskRead = 'task_read',
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/rmf-app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { UserProfileProvider } from 'rmf-auth';

import { AppConfigContext, AuthenticatorContext } from '../../app-config';
import { UserProfileProvider } from '../../auth';
import { RmfIngress } from './rmf-ingress';

export * from './rmf-ingress';
Expand Down
2 changes: 1 addition & 1 deletion packages/dashboard/src/components/rmf-app/rmf-ingress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import {
TaskStateOutput as TaskState,
} from 'api-client';
import axios from 'axios';
import { Authenticator } from 'rmf-auth';
import { map, Observable, shareReplay } from 'rxjs';

import { AppConfig } from '../../app-config';
import { Authenticator } from '../../auth';
import { NegotiationStatusManager } from '../../managers/negotiation-status-manager';
import {
DefaultTrajectoryManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Button, ButtonProps, Tooltip, Typography } from '@mui/material';
import { TaskStateOutput as TaskState } from 'api-client';
import React from 'react';
import { ConfirmationDialog } from 'react-components';
import { UserProfile, UserProfileContext } from 'rmf-auth';

import { UserProfile, UserProfileContext } from '../../auth';
import { AppControllerContext } from '../app-contexts';
import { AppEvents } from '../app-events';
import { Enforcer } from '../permissions';
Expand Down
12 changes: 6 additions & 6 deletions packages/dashboard/src/components/tests/appbar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { StubAuthenticator, UserProfile, UserProfileContext } from 'rmf-auth';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { AuthenticatorContext, Resources, ResourcesContext } from '../../app-config';
import { StubAuthenticator, UserProfile, UserProfileContext } from '../../auth';
import { AppController, AppControllerContext } from '../app-contexts';
import AppBar from '../appbar';
import { render } from '../tests/test-utils';
Expand All @@ -24,7 +24,7 @@ describe('AppBar', () => {
appController = makeMockAppController();
});

test('renders with navigation bar', () => {
it('renders with navigation bar', () => {
const root = render(
<Base>
<AppBar />
Expand All @@ -33,7 +33,7 @@ describe('AppBar', () => {
expect(root.getAllByRole('tablist').length > 0).toBeTruthy();
});

test('user button is shown when there is an authenticated user', () => {
it('user button is shown when there is an authenticated user', () => {
const profile: UserProfile = {
user: { username: 'test', is_admin: false, roles: [] },
permissions: [],
Expand All @@ -48,7 +48,7 @@ describe('AppBar', () => {
expect(root.getByLabelText('user-btn')).toBeTruthy();
});

test('logout is triggered when logout button is clicked', async () => {
it('logout is triggered when logout button is clicked', async () => {
const authenticator = new StubAuthenticator('test');
const spy = vi.spyOn(authenticator, 'logout').mockImplementation(() => undefined as any);
const profile: UserProfile = {
Expand All @@ -70,7 +70,7 @@ describe('AppBar', () => {
await expect(waitFor(() => expect(spy).toHaveBeenCalledTimes(1))).resolves.not.toThrow();
});

test('uses headerLogo from logo resources manager', async () => {
it('uses headerLogo from logo resources manager', async () => {
const resources: Resources = {
fleets: {},
logos: {
Expand Down
Loading

0 comments on commit b4c2b06

Please sign in to comment.