From: Pawel Kromplewski Date: Mon, 3 Dec 2018 07:29:41 +0000 (+0100) Subject: Merge branch 'master' into 14452-my-account X-Git-Tag: 1.4.0~92^2~6 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/4ea2ff188ec745966387ce8bbe14880bfeede863?hp=-c Merge branch 'master' into 14452-my-account refs #14452 Arvados-DCO-1.1-Signed-off-by: Pawel Kromplewski --- 4ea2ff188ec745966387ce8bbe14880bfeede863 diff --combined src/models/user.ts index eed135a2,9f9c5347..dfb41889 --- a/src/models/user.ts +++ b/src/models/user.ts @@@ -4,24 -4,13 +4,25 @@@ import { Resource, ResourceKind } from '~/models/resource'; +export type userPrefs = { + profile?: { + organization?: string, + organization_email?: string, + lab?: string, + website_url?: string, + role?: string + } +}; + export interface User { email: string; firstName: string; lastName: string; uuid: string; ownerUuid: string; + identityUrl: string; + prefs: userPrefs; + isAdmin: boolean; } export const getUserFullname = (user?: User) => { diff --combined src/routes/route-change-handlers.ts index f2aed2db,22d0b7c7..1f3bd93f --- a/src/routes/route-change-handlers.ts +++ b/src/routes/route-change-handlers.ts @@@ -4,8 -4,8 +4,10 @@@ import { History, Location } from 'history'; import { RootStore } from '~/store/store'; +import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchRunProcessRoute, matchWorkflowRoute, matchSearchResultsRoute, matchSshKeysRoute, matchRepositoriesRoute, matchMyAccountRoute } from './routes'; +import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog, loadSshKeys, loadRepositories, loadMyAccount } from '~/store/workbench/workbench-actions'; + import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchRunProcessRoute, matchWorkflowRoute, matchSearchResultsRoute, matchSshKeysRoute, matchRepositoriesRoute, matchVirtualMachineRoute } from './routes'; + import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog, loadSshKeys, loadRepositories, loadVirtualMachines } from '~/store/workbench/workbench-actions'; import { navigateToRootProject } from '~/store/navigation/navigation-action'; import { loadSharedWithMe, loadRunProcess, loadWorkflow, loadSearchResults } from '~//store/workbench/workbench-actions'; @@@ -27,9 -27,9 +29,10 @@@ const handleLocationChange = (store: Ro const searchResultsMatch = matchSearchResultsRoute(pathname); const sharedWithMeMatch = matchSharedWithMeRoute(pathname); const runProcessMatch = matchRunProcessRoute(pathname); + const virtualMachineMatch = matchVirtualMachineRoute(pathname); const workflowMatch = matchWorkflowRoute(pathname); const sshKeysMatch = matchSshKeysRoute(pathname); + const myAccountMatch = matchMyAccountRoute(pathname); if (projectMatch) { store.dispatch(loadProject(projectMatch.params.id)); @@@ -53,11 -53,11 +56,13 @@@ store.dispatch(loadWorkflow); } else if (searchResultsMatch) { store.dispatch(loadSearchResults); + } else if (virtualMachineMatch) { + store.dispatch(loadVirtualMachines); } else if(repositoryMatch) { store.dispatch(loadRepositories); } else if (sshKeysMatch) { store.dispatch(loadSshKeys); + } else if (myAccountMatch) { + store.dispatch(loadMyAccount); } }; diff --combined src/routes/routes.ts index 988b4cb4,71cdfdac..a27b4274 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@@ -19,10 -19,10 +19,11 @@@ export const Routes = REPOSITORIES: '/repositories', SHARED_WITH_ME: '/shared-with-me', RUN_PROCESS: '/run-process', + VIRTUAL_MACHINES: '/virtual-machines', WORKFLOWS: '/workflows', SEARCH_RESULTS: '/search-results', - SSH_KEYS: `/ssh-keys` + SSH_KEYS: `/ssh-keys`, + MY_ACCOUNT: '/my-account' }; export const getResourceUrl = (uuid: string) => { @@@ -80,11 -80,11 +81,14 @@@ export const matchWorkflowRoute = (rout export const matchSearchResultsRoute = (route: string) => matchPath(route, { path: Routes.SEARCH_RESULTS }); + export const matchVirtualMachineRoute = (route: string) => + matchPath(route, { path: Routes.VIRTUAL_MACHINES }); + export const matchRepositoriesRoute = (route: string) => matchPath(route, { path: Routes.REPOSITORIES }); export const matchSshKeysRoute = (route: string) => matchPath(route, { path: Routes.SSH_KEYS }); + +export const matchMyAccountRoute = (route: string) => + matchPath(route, { path: Routes.MY_ACCOUNT }); diff --combined src/services/auth-service/auth-service.ts index d4e81e42,edc6e24f..6faaf99e --- a/src/services/auth-service/auth-service.ts +++ b/src/services/auth-service/auth-service.ts @@@ -2,7 -2,7 +2,7 @@@ // // SPDX-License-Identifier: AGPL-3.0 -import { User } from "~/models/user"; +import { User, userPrefs } from "~/models/user"; import { AxiosInstance } from "axios"; import { ApiActions, ProgressFn } from "~/services/api/api-actions"; import * as uuid from "uuid/v4"; @@@ -13,8 -13,7 +13,9 @@@ export const USER_FIRST_NAME_KEY = 'use export const USER_LAST_NAME_KEY = 'userLastName'; export const USER_UUID_KEY = 'userUuid'; export const USER_OWNER_UUID_KEY = 'userOwnerUuid'; + export const USER_IS_ADMIN = 'isAdmin'; +export const USER_IDENTITY_URL = 'identityUrl'; +export const USER_PREFS = 'prefs'; export interface UserDetailsResponse { email: string; @@@ -23,8 -22,6 +24,8 @@@ uuid: string; owner_uuid: string; is_admin: boolean; + identity_url: string; + prefs: userPrefs; } export class AuthService { @@@ -54,16 -51,20 +55,22 @@@ return localStorage.getItem(USER_OWNER_UUID_KEY) || undefined; } + public getIsAdmin(): boolean { + return !!localStorage.getItem(USER_IS_ADMIN); + } + public getUser(): User | undefined { const email = localStorage.getItem(USER_EMAIL_KEY); const firstName = localStorage.getItem(USER_FIRST_NAME_KEY); const lastName = localStorage.getItem(USER_LAST_NAME_KEY); - const uuid = localStorage.getItem(USER_UUID_KEY); - const ownerUuid = localStorage.getItem(USER_OWNER_UUID_KEY); + const uuid = this.getUuid(); + const ownerUuid = this.getOwnerUuid(); + const isAdmin = this.getIsAdmin(); + const identityUrl = localStorage.getItem(USER_IDENTITY_URL); + const prefs = JSON.parse(localStorage.getItem(USER_PREFS) || '{"profile": {}}'); + - return email && firstName && lastName && uuid && ownerUuid - ? { email, firstName, lastName, uuid, ownerUuid, isAdmin } + return email && firstName && lastName && uuid && ownerUuid && identityUrl && prefs - ? { email, firstName, lastName, uuid, ownerUuid, identityUrl, prefs } ++ ? { email, firstName, lastName, uuid, ownerUuid, isAdmin, identityUrl, prefs } : undefined; } @@@ -73,8 -74,7 +80,9 @@@ localStorage.setItem(USER_LAST_NAME_KEY, user.lastName); localStorage.setItem(USER_UUID_KEY, user.uuid); localStorage.setItem(USER_OWNER_UUID_KEY, user.ownerUuid); + localStorage.setItem(USER_IS_ADMIN, JSON.stringify(user.isAdmin)); + localStorage.setItem(USER_IDENTITY_URL, user.identityUrl); + localStorage.setItem(USER_PREFS, JSON.stringify(user.prefs)); } public removeUser() { @@@ -83,8 -83,7 +91,9 @@@ localStorage.removeItem(USER_LAST_NAME_KEY); localStorage.removeItem(USER_UUID_KEY); localStorage.removeItem(USER_OWNER_UUID_KEY); + localStorage.removeItem(USER_IS_ADMIN); + localStorage.removeItem(USER_IDENTITY_URL); + localStorage.removeItem(USER_PREFS); } public login() { @@@ -104,16 -103,13 +113,17 @@@ .get('/users/current') .then(resp => { this.actions.progressFn(reqId, false); + const prefs = resp.data.prefs.profile ? resp.data.prefs : { profile: {}}; + console.log(resp.data); return { email: resp.data.email, firstName: resp.data.first_name, lastName: resp.data.last_name, uuid: resp.data.uuid, ownerUuid: resp.data.owner_uuid, - isAdmin: resp.data.is_admin ++ isAdmin: resp.data.is_admin, + identityUrl: resp.data.identity_url, + prefs }; }) .catch(e => { diff --combined src/store/auth/auth-reducer.test.ts index 592191ca,a4017db3..b9f768f1 --- a/src/store/auth/auth-reducer.test.ts +++ b/src/store/auth/auth-reducer.test.ts @@@ -30,13 -30,13 +30,15 @@@ describe('auth-reducer', () => lastName: "Doe", uuid: "uuid", ownerUuid: "ownerUuid", + identityUrl: "identityUrl", - prefs: {} ++ prefs: {}, + isAdmin: false }; const state = reducer(initialState, authActions.INIT({ user, token: "token" })); expect(state).toEqual({ apiToken: "token", - user + user, + sshKeys: [] }); }); @@@ -60,8 -60,7 +62,9 @@@ lastName: "Doe", uuid: "uuid", ownerUuid: "ownerUuid", + identityUrl: "identityUrl", - prefs: {} ++ prefs: {}, + isAdmin: false }; const state = reducer(initialState, authActions.USER_DETAILS_SUCCESS(user)); @@@ -74,6 -73,7 +77,7 @@@ lastName: "Doe", uuid: "uuid", ownerUuid: "ownerUuid", + isAdmin: false } }); }); diff --combined src/store/navigation/navigation-action.ts index 0b6713bf,2bfd8b99..80a7f213 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@@ -62,8 -62,8 +62,10 @@@ export const navigateToRunProcess = pus export const navigateToSearchResults = push(Routes.SEARCH_RESULTS); + export const navigateToVirtualMachines = push(Routes.VIRTUAL_MACHINES); + export const navigateToRepositories = push(Routes.REPOSITORIES); export const navigateToSshKeys= push(Routes.SSH_KEYS); + +export const navigateToMyAccount = push(Routes.MY_ACCOUNT); diff --combined src/store/workbench/workbench-actions.ts index dc54e4b8,12dbe7b1..091a8ccc --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@@ -40,7 -40,6 +40,7 @@@ import { loadSharedWithMePanel } from ' import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog'; import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions'; import { loadSshKeysPanel } from '~/store/auth/auth-action'; +import { loadMyAccountPanel } from '~/store/my-account/my-account-panel-actions'; import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view'; import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions'; import { getProgressIndicator } from '~/store/progress-indicator/progress-indicator-reducer'; @@@ -55,6 -54,7 +55,7 @@@ import { collectionPanelActions } from import { CollectionResource } from "~/models/collection"; import { searchResultsPanelActions, loadSearchResultsPanel } from '~/store/search-results-panel/search-results-panel-actions'; import { searchResultsPanelColumns } from '~/views/search-results-panel/search-results-panel-view'; + import { loadVirtualMachinesPanel } from '~/store/virtual-machines/virtual-machines-actions'; import { loadRepositoriesPanel } from '~/store/repositories/repositories-actions'; export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen'; @@@ -393,6 -393,12 +394,12 @@@ export const loadSearchResults = handle await dispatch(loadSearchResultsPanel()); }); + export const loadVirtualMachines = handleFirstTimeLoad( + async (dispatch: Dispatch) => { + await dispatch(loadVirtualMachinesPanel()); + dispatch(setBreadcrumbs([{ label: 'Virtual Machines' }])); + }); + export const loadRepositories = handleFirstTimeLoad( async (dispatch: Dispatch) => { await dispatch(loadRepositoriesPanel()); @@@ -404,11 -410,6 +411,11 @@@ export const loadSshKeys = handleFirstT await dispatch(loadSshKeysPanel()); }); +export const loadMyAccount = handleFirstTimeLoad( + async (dispatch: Dispatch) => { + await dispatch(loadMyAccountPanel()); + }); + const finishLoadingProject = (project: GroupContentsResource | string) => async (dispatch: Dispatch) => { const uuid = typeof project === 'string' ? project : project.uuid; diff --combined src/views-components/main-app-bar/account-menu.tsx index b93bfcfb,ca88021c..0aedee90 --- a/src/views-components/main-app-bar/account-menu.tsx +++ b/src/views-components/main-app-bar/account-menu.tsx @@@ -12,7 -12,8 +12,8 @@@ import { logout } from '~/store/auth/au import { RootState } from "~/store/store"; import { openCurrentTokenDialog } from '~/store/current-token-dialog/current-token-dialog-actions'; import { openRepositoriesPanel } from "~/store/repositories/repositories-actions"; -import { navigateToSshKeys } from '~/store/navigation/navigation-action'; +import { navigateToSshKeys, navigateToMyAccount } from '~/store/navigation/navigation-action'; + import { openVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions"; interface AccountMenuProps { user?: User; @@@ -32,10 -33,11 +33,11 @@@ export const AccountMenu = connect(mapS {getUserFullname(user)} + dispatch(openVirtualMachines())}>Virtual Machines dispatch(openRepositoriesPanel())}>Repositories dispatch(openCurrentTokenDialog)}>Current token dispatch(navigateToSshKeys)}>Ssh Keys - My account + dispatch(navigateToMyAccount)}>My account dispatch(logout())}>Logout : null); diff --combined src/views/workbench/workbench.tsx index 8542e6ca,3914f646..2cff4317 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@@ -45,10 -45,10 +45,11 @@@ import SplitterLayout from 'react-split import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel'; import { SearchResultsPanel } from '~/views/search-results-panel/search-results-panel'; import { SshKeyPanel } from '~/views/ssh-key-panel/ssh-key-panel'; +import { MyAccountPanel } from '~/views/my-account-panel/my-account-panel'; import { SharingDialog } from '~/views-components/sharing-dialog/sharing-dialog'; import { AdvancedTabDialog } from '~/views-components/advanced-tab-dialog/advanced-tab-dialog'; import { ProcessInputDialog } from '~/views-components/process-input-dialog/process-input-dialog'; + import { VirtualMachinePanel } from '~/views/virtual-machine-panel/virtual-machine-panel'; import { ProjectPropertiesDialog } from '~/views-components/project-properties-dialog/project-properties-dialog'; import { RepositoriesPanel } from '~/views/repositories-panel/repositories-panel'; import { RepositoriesSampleGitDialog } from '~/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog'; @@@ -128,9 -128,9 +129,10 @@@ export const WorkbenchPanel + +