From: Pawel Kromplewski Date: Wed, 5 Dec 2018 15:13:43 +0000 (+0100) Subject: Merge branch 'master' into 14452-my-account X-Git-Tag: 1.4.0~92^2~3 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/a1e2b8ba77e4a7273940a3fc542bc42e282618a7?hp=-c Merge branch 'master' into 14452-my-account refs #14452 Arvados-DCO-1.1-Signed-off-by: Pawel Kromplewski --- a1e2b8ba77e4a7273940a3fc542bc42e282618a7 diff --combined src/routes/route-change-handlers.ts index 400866e3,f2304aca..68de3107 --- a/src/routes/route-change-handlers.ts +++ b/src/routes/route-change-handlers.ts @@@ -4,10 -4,9 +4,9 @@@ 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, matchVirtualMachineRoute } from './routes'; - import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog, loadSshKeys, loadRepositories, loadMyAccount, loadVirtualMachines } from '~/store/workbench/workbench-actions'; + import * as Routes from '~/routes/routes'; + import * as WorkbenchActions from '~/store/workbench/workbench-actions'; import { navigateToRootProject } from '~/store/navigation/navigation-action'; - import { loadSharedWithMe, loadRunProcess, loadWorkflow, loadSearchResults } from '~//store/workbench/workbench-actions'; export const addRouteChangeHandlers = (history: History, store: RootStore) => { const handler = handleLocationChange(store); @@@ -16,51 -15,54 +15,57 @@@ }; const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => { - const rootMatch = matchRootRoute(pathname); - const projectMatch = matchProjectRoute(pathname); - const collectionMatch = matchCollectionRoute(pathname); - const favoriteMatch = matchFavoritesRoute(pathname); - const trashMatch = matchTrashRoute(pathname); - const processMatch = matchProcessRoute(pathname); - const processLogMatch = matchProcessLogRoute(pathname); - const repositoryMatch = matchRepositoriesRoute(pathname); - 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); + const rootMatch = Routes.matchRootRoute(pathname); + const projectMatch = Routes.matchProjectRoute(pathname); + const collectionMatch = Routes.matchCollectionRoute(pathname); + const favoriteMatch = Routes.matchFavoritesRoute(pathname); + const trashMatch = Routes.matchTrashRoute(pathname); + const processMatch = Routes.matchProcessRoute(pathname); + const processLogMatch = Routes.matchProcessLogRoute(pathname); + const repositoryMatch = Routes.matchRepositoriesRoute(pathname); + const searchResultsMatch = Routes.matchSearchResultsRoute(pathname); + const sharedWithMeMatch = Routes.matchSharedWithMeRoute(pathname); + const runProcessMatch = Routes.matchRunProcessRoute(pathname); + const virtualMachineMatch = Routes.matchVirtualMachineRoute(pathname); + const workflowMatch = Routes.matchWorkflowRoute(pathname); + const sshKeysMatch = Routes.matchSshKeysRoute(pathname); + const keepServicesMatch = Routes.matchKeepServicesRoute(pathname); + const computeNodesMatch = Routes.matchComputeNodesRoute(pathname); ++ const myAccountMatch = Routes.matchMyAccountRoute(pathname); if (projectMatch) { - store.dispatch(loadProject(projectMatch.params.id)); + store.dispatch(WorkbenchActions.loadProject(projectMatch.params.id)); } else if (collectionMatch) { - store.dispatch(loadCollection(collectionMatch.params.id)); + store.dispatch(WorkbenchActions.loadCollection(collectionMatch.params.id)); } else if (favoriteMatch) { - store.dispatch(loadFavorites()); + store.dispatch(WorkbenchActions.loadFavorites()); } else if (trashMatch) { - store.dispatch(loadTrash()); + store.dispatch(WorkbenchActions.loadTrash()); } else if (processMatch) { - store.dispatch(loadProcess(processMatch.params.id)); + store.dispatch(WorkbenchActions.loadProcess(processMatch.params.id)); } else if (processLogMatch) { - store.dispatch(loadProcessLog(processLogMatch.params.id)); + store.dispatch(WorkbenchActions.loadProcessLog(processLogMatch.params.id)); } else if (rootMatch) { store.dispatch(navigateToRootProject); } else if (sharedWithMeMatch) { - store.dispatch(loadSharedWithMe); + store.dispatch(WorkbenchActions.loadSharedWithMe); } else if (runProcessMatch) { - store.dispatch(loadRunProcess); + store.dispatch(WorkbenchActions.loadRunProcess); } else if (workflowMatch) { - store.dispatch(loadWorkflow); + store.dispatch(WorkbenchActions.loadWorkflow); } else if (searchResultsMatch) { - store.dispatch(loadSearchResults); + store.dispatch(WorkbenchActions.loadSearchResults); } else if (virtualMachineMatch) { - store.dispatch(loadVirtualMachines); + store.dispatch(WorkbenchActions.loadVirtualMachines); } else if(repositoryMatch) { - store.dispatch(loadRepositories); + store.dispatch(WorkbenchActions.loadRepositories); } else if (sshKeysMatch) { - store.dispatch(loadSshKeys); + store.dispatch(WorkbenchActions.loadSshKeys); + } else if (keepServicesMatch) { + store.dispatch(WorkbenchActions.loadKeepServices); + } else if (computeNodesMatch) { + store.dispatch(WorkbenchActions.loadComputeNodes); + } else if (myAccountMatch) { - store.dispatch(loadMyAccount); ++ store.dispatch(WorkbenchActions.loadMyAccount); } }; diff --combined src/routes/routes.ts index a27b4274,8f8fa06b..71d920ab --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@@ -23,7 -23,8 +23,9 @@@ export const Routes = WORKFLOWS: '/workflows', SEARCH_RESULTS: '/search-results', SSH_KEYS: `/ssh-keys`, - MY_ACCOUNT: '/my-account' ++ MY_ACCOUNT: '/my-account', + KEEP_SERVICES: `/keep-services`, + COMPUTE_NODES: `/nodes` }; export const getResourceUrl = (uuid: string) => { @@@ -90,5 -91,8 +92,11 @@@ export const matchRepositoriesRoute = ( export const matchSshKeysRoute = (route: string) => matchPath(route, { path: Routes.SSH_KEYS }); +export const matchMyAccountRoute = (route: string) => + matchPath(route, { path: Routes.MY_ACCOUNT }); ++ + export const matchKeepServicesRoute = (route: string) => + matchPath(route, { path: Routes.KEEP_SERVICES }); + + export const matchComputeNodesRoute = (route: string) => + matchPath(route, { path: Routes.COMPUTE_NODES }); diff --combined src/services/auth-service/auth-service.ts index 6faaf99e,98c03215..8c2ad5ca --- a/src/services/auth-service/auth-service.ts +++ b/src/services/auth-service/auth-service.ts @@@ -2,9 -2,9 +2,9 @@@ // // 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 { ApiActions } from "~/services/api/api-actions"; import * as uuid from "uuid/v4"; export const API_TOKEN_KEY = 'apiToken'; @@@ -14,8 -14,6 +14,8 @@@ export const USER_LAST_NAME_KEY = 'user 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; @@@ -24,8 -22,6 +24,8 @@@ uuid: string; owner_uuid: string; is_admin: boolean; + identity_url: string; + prefs: userPrefs; } export class AuthService { @@@ -56,7 -52,7 +56,7 @@@ } public getIsAdmin(): boolean { - return !!localStorage.getItem(USER_IS_ADMIN); + return localStorage.getItem(USER_IS_ADMIN) === 'true'; } public getUser(): User | undefined { @@@ -65,12 -61,10 +65,12 @@@ const lastName = localStorage.getItem(USER_LAST_NAME_KEY); const uuid = this.getUuid(); const ownerUuid = this.getOwnerUuid(); - const isAdmin = this.getIsAdmin(); + 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, isAdmin, identityUrl, prefs } : undefined; } @@@ -81,8 -75,6 +81,8 @@@ 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() { @@@ -92,8 -84,6 +92,8 @@@ 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() { @@@ -113,17 -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-action.ts index a2046f33,e1b36f82..0ec39ebc --- a/src/store/auth/auth-action.ts +++ b/src/store/auth/auth-action.ts @@@ -94,9 -94,9 +94,9 @@@ export const openSshKeyCreateDialog = ( export const openPublicKeyDialog = (name: string, publicKey: string) => dialogActions.OPEN_DIALOG({ id: SSH_KEY_PUBLIC_KEY_DIALOG, data: { name, publicKey } }); - export const openSshKeyAttributesDialog = (index: number) => + export const openSshKeyAttributesDialog = (uuid: string) => (dispatch: Dispatch, getState: () => RootState) => { - const sshKey = getState().auth.sshKeys[index]; + const sshKey = getState().auth.sshKeys.find(it => it.uuid === uuid); dispatch(dialogActions.OPEN_DIALOG({ id: SSH_KEY_ATTRIBUTES_DIALOG, data: { sshKey } })); }; @@@ -161,4 -161,5 +161,4 @@@ export const loadSshKeysPanel = () = } }; - export type AuthAction = UnionOf; diff --combined src/store/navigation/navigation-action.ts index 80a7f213,50cfd88d..a3652726 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@@ -68,4 -68,6 +68,8 @@@ export const navigateToRepositories = p export const navigateToSshKeys= push(Routes.SSH_KEYS); +export const navigateToMyAccount = push(Routes.MY_ACCOUNT); ++ + export const navigateToKeepServices = push(Routes.KEEP_SERVICES); + + export const navigateToComputeNodes = push(Routes.COMPUTE_NODES); diff --combined src/store/workbench/workbench-actions.ts index 091a8ccc,e3f96a9c..9d0140f3 --- 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'; @@@ -57,6 -56,8 +57,8 @@@ import { searchResultsPanelActions, loa 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'; + import { loadKeepServicesPanel } from '~/store/keep-services/keep-services-actions'; + import { loadComputeNodesPanel } from '~/store/compute-nodes/compute-nodes-actions'; export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen'; @@@ -411,11 -412,16 +413,21 @@@ export const loadSshKeys = handleFirstT await dispatch(loadSshKeysPanel()); }); +export const loadMyAccount = handleFirstTimeLoad( + async (dispatch: Dispatch) => { + await dispatch(loadMyAccountPanel()); + }); + + export const loadKeepServices = handleFirstTimeLoad( + async (dispatch: Dispatch) => { + await dispatch(loadKeepServicesPanel()); + }); + + export const loadComputeNodes = handleFirstTimeLoad( + async (dispatch: Dispatch) => { + await dispatch(loadComputeNodesPanel()); + }); + 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 0aedee90,f4232a12..ee726f3d --- a/src/views-components/main-app-bar/account-menu.tsx +++ b/src/views-components/main-app-bar/account-menu.tsx @@@ -12,7 -12,7 +12,7 @@@ 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, navigateToMyAccount } from '~/store/navigation/navigation-action'; -import { navigateToSshKeys, navigateToKeepServices, navigateToComputeNodes } from '~/store/navigation/navigation-action'; ++import { navigateToSshKeys, navigateToKeepServices, navigateToComputeNodes, navigateToMyAccount } from '~/store/navigation/navigation-action'; import { openVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions"; interface AccountMenuProps { @@@ -37,7 -37,9 +37,9 @@@ export const AccountMenu = connect(mapS dispatch(openRepositoriesPanel())}>Repositories dispatch(openCurrentTokenDialog)}>Current token dispatch(navigateToSshKeys)}>Ssh Keys + { user.isAdmin && dispatch(navigateToKeepServices)}>Keep Services } + { user.isAdmin && dispatch(navigateToComputeNodes)}>Compute Nodes } - My account + dispatch(navigateToMyAccount)}>My account dispatch(logout())}>Logout : null); diff --combined src/views/workbench/workbench.tsx index 2cff4317,92c2438b..5efffa19 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@@ -45,21 -45,28 +45,29 @@@ 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 { KeepServicePanel } from '~/views/keep-service-panel/keep-service-panel'; + import { ComputeNodePanel } from '~/views/compute-node-panel/compute-node-panel'; import { RepositoriesSampleGitDialog } from '~/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog'; import { RepositoryAttributesDialog } from '~/views-components/repository-attributes-dialog/repository-attributes-dialog'; import { CreateRepositoryDialog } from '~/views-components/dialog-forms/create-repository-dialog'; import { RemoveRepositoryDialog } from '~/views-components/repository-remove-dialog/repository-remove-dialog'; import { CreateSshKeyDialog } from '~/views-components/dialog-forms/create-ssh-key-dialog'; import { PublicKeyDialog } from '~/views-components/ssh-keys-dialog/public-key-dialog'; + import { RemoveComputeNodeDialog } from '~/views-components/compute-nodes-dialog/remove-dialog'; + import { RemoveKeepServiceDialog } from '~/views-components/keep-services-dialog/remove-dialog'; import { RemoveSshKeyDialog } from '~/views-components/ssh-keys-dialog/remove-dialog'; + import { RemoveVirtualMachineDialog } from '~/views-components/virtual-machines-dialog/remove-dialog'; + import { AttributesComputeNodeDialog } from '~/views-components/compute-nodes-dialog/attributes-dialog'; + import { AttributesKeepServiceDialog } from '~/views-components/keep-services-dialog/attributes-dialog'; import { AttributesSshKeyDialog } from '~/views-components/ssh-keys-dialog/attributes-dialog'; + import { VirtualMachineAttributesDialog } from '~/views-components/virtual-machines-dialog/attributes-dialog'; type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content'; @@@ -132,7 -139,8 +140,9 @@@ export const WorkbenchPanel + + + @@@ -142,6 -150,8 +152,8 @@@ + + @@@ -163,9 -173,12 +175,12 @@@ + + + @@@ -175,5 -188,6 +190,6 @@@ + );