From: Pawel Kowalczyk Date: Fri, 7 Dec 2018 10:05:13 +0000 (+0100) Subject: next conflicts X-Git-Tag: 1.4.0~93^2 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/cda10815df30e3bceec728535f426754ef8ff2e8 next conflicts Feature #14504 Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk --- cda10815df30e3bceec728535f426754ef8ff2e8 diff --cc src/index.tsx index 87af8f1d,c33ef7c1..8f702af1 --- a/src/index.tsx +++ b/src/index.tsx @@@ -53,8 -53,8 +53,9 @@@ import { sshKeyActionSet } from '~/view import { keepServiceActionSet } from '~/views-components/context-menu/action-sets/keep-service-action-set'; import { loadVocabulary } from '~/store/vocabulary/vocabulary-actions'; import { virtualMachineActionSet } from '~/views-components/context-menu/action-sets/virtual-machine-action-set'; +import { userActionSet } from '~/views-components/context-menu/action-sets/user-action-set'; import { computeNodeActionSet } from '~/views-components/context-menu/action-sets/compute-node-action-set'; + import { apiClientAuthorizationActionSet } from '~/views-components/context-menu/action-sets/api-client-authorization-action-set'; console.log(`Starting arvados [${getBuildInfo()}]`); @@@ -75,8 -75,8 +76,9 @@@ addMenuActionSet(ContextMenuKind.REPOSI addMenuActionSet(ContextMenuKind.SSH_KEY, sshKeyActionSet); addMenuActionSet(ContextMenuKind.VIRTUAL_MACHINE, virtualMachineActionSet); addMenuActionSet(ContextMenuKind.KEEP_SERVICE, keepServiceActionSet); +addMenuActionSet(ContextMenuKind.USER, userActionSet); addMenuActionSet(ContextMenuKind.NODE, computeNodeActionSet); + addMenuActionSet(ContextMenuKind.API_CLIENT_AUTHORIZATION, apiClientAuthorizationActionSet); fetchConfig() .then(({ config, apiHost }) => { diff --cc src/routes/route-change-handlers.ts index 5b281b83,a733e42f..e2454d63 --- a/src/routes/route-change-handlers.ts +++ b/src/routes/route-change-handlers.ts @@@ -31,8 -31,8 +31,9 @@@ const handleLocationChange = (store: Ro const sshKeysMatch = Routes.matchSshKeysRoute(pathname); const keepServicesMatch = Routes.matchKeepServicesRoute(pathname); const computeNodesMatch = Routes.matchComputeNodesRoute(pathname); + const apiClientAuthorizationsMatch = Routes.matchApiClientAuthorizationsRoute(pathname); const myAccountMatch = Routes.matchMyAccountRoute(pathname); + const userMatch = Routes.matchUsersRoute(pathname); if (projectMatch) { store.dispatch(WorkbenchActions.loadProject(projectMatch.params.id)); @@@ -66,9 -66,9 +67,11 @@@ store.dispatch(WorkbenchActions.loadKeepServices); } else if (computeNodesMatch) { store.dispatch(WorkbenchActions.loadComputeNodes); + } else if (apiClientAuthorizationsMatch) { + store.dispatch(WorkbenchActions.loadApiClientAuthorizations); } else if (myAccountMatch) { store.dispatch(WorkbenchActions.loadMyAccount); + }else if (userMatch) { + store.dispatch(WorkbenchActions.loadUsers); } }; diff --cc src/routes/routes.ts index dabb9bf5,7f15a8de..88dfd469 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@@ -26,7 -26,7 +26,8 @@@ export const Routes = MY_ACCOUNT: '/my-account', KEEP_SERVICES: `/keep-services`, COMPUTE_NODES: `/nodes`, - USERS: '/users' ++ USERS: '/users', + API_CLIENT_AUTHORIZATIONS: `/api_client_authorizations` }; export const getResourceUrl = (uuid: string) => { @@@ -99,8 -99,8 +100,11 @@@ export const matchMyAccountRoute = (rou export const matchKeepServicesRoute = (route: string) => matchPath(route, { path: Routes.KEEP_SERVICES }); +export const matchUsersRoute = (route: string) => + matchPath(route, { path: Routes.USERS }); + export const matchComputeNodesRoute = (route: string) => matchPath(route, { path: Routes.COMPUTE_NODES }); + + export const matchApiClientAuthorizationsRoute = (route: string) => - matchPath(route, { path: Routes.API_CLIENT_AUTHORIZATIONS }); ++ matchPath(route, { path: Routes.API_CLIENT_AUTHORIZATIONS }); diff --cc src/store/advanced-tab/advanced-tab.ts index 4da4d7e1,a77ffcca..851eb949 --- a/src/store/advanced-tab/advanced-tab.ts +++ b/src/store/advanced-tab/advanced-tab.ts @@@ -74,8 -75,8 +75,9 @@@ enum ResourcePrefix AUTORIZED_KEYS = 'authorized_keys', VIRTUAL_MACHINES = 'virtual_machines', KEEP_SERVICES = 'keep_services', + COMPUTE_NODES = 'nodes', + USERS = 'users', - COMPUTE_NODES = 'nodes' + API_CLIENT_AUTHORIZATIONS = 'api_client_authorizations' } enum KeepServiceData { @@@ -93,9 -89,14 +95,14 @@@ enum ComputeNodeData PROPERTIES = 'properties' } - type AdvanceResourceKind = CollectionData | ProcessData | ProjectData | RepositoryData | SshKeyData | VirtualMachineData | KeepServiceData | ComputeNodeData | UserData; + enum ApiClientAuthorizationsData { + API_CLIENT_AUTHORIZATION = 'api_client_authorization', + DEFAULT_OWNER_UUID = 'default_owner_uuid' + } + -type AdvanceResourceKind = CollectionData | ProcessData | ProjectData | RepositoryData | SshKeyData | VirtualMachineData | KeepServiceData | ComputeNodeData | ApiClientAuthorizationsData; ++type AdvanceResourceKind = CollectionData | ProcessData | ProjectData | RepositoryData | SshKeyData | VirtualMachineData | KeepServiceData | ComputeNodeData | ApiClientAuthorizationsData | UserData; type AdvanceResourcePrefix = GroupContentsResourcePrefix | ResourcePrefix; - type AdvanceResponseData = ContainerRequestResource | ProjectResource | CollectionResource | RepositoryResource | SshKeyResource | VirtualMachinesResource | KeepServiceResource | NodeResource | UserResource | undefined; -type AdvanceResponseData = ContainerRequestResource | ProjectResource | CollectionResource | RepositoryResource | SshKeyResource | VirtualMachinesResource | KeepServiceResource | NodeResource | ApiClientAuthorization | undefined; ++type AdvanceResponseData = ContainerRequestResource | ProjectResource | CollectionResource | RepositoryResource | SshKeyResource | VirtualMachinesResource | KeepServiceResource | NodeResource | ApiClientAuthorization | UserResource | undefined; export const openAdvancedTabDialog = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { @@@ -538,5 -509,27 +560,27 @@@ const computeNodeApiResponse = (apiResp "properties": "${JSON.stringify(properties, null, 4)}", "info": "${JSON.stringify(info, null, 4)}"`; + return response; + }; + + const apiClientAuthorizationApiResponse = (apiResponse: ApiClientAuthorization) => { + const { - uuid, ownerUuid, apiToken, apiClientId, userId, createdByIpAddress, lastUsedByIpAddress, ++ uuid, ownerUuid, apiToken, apiClientId, userId, createdByIpAddress, lastUsedByIpAddress, + lastUsedAt, expiresAt, defaultOwnerUuid, scopes, updatedAt, createdAt + } = apiResponse; + const response = `"uuid": "${uuid}", + "owner_uuid": "${ownerUuid}", + "api_token": "${stringify(apiToken)}", + "api_client_id": "${stringify(apiClientId)}", + "user_id": "${stringify(userId)}", + "created_by_ip_address": "${stringify(createdByIpAddress)}", + "last_used_by_ip_address": "${stringify(lastUsedByIpAddress)}", + "last_used_at": "${stringify(lastUsedAt)}", + "expires_at": "${stringify(expiresAt)}", + "created_at": "${stringify(createdAt)}", + "updated_at": "${stringify(updatedAt)}", + "default_owner_uuid": "${stringify(defaultOwnerUuid)}", + "scopes": "${JSON.stringify(scopes, null, 4)}"`; + return response; }; diff --cc src/store/navigation/navigation-action.ts index bae16891,067a9ac4..8d68a4b6 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@@ -74,4 -74,4 +74,6 @@@ export const navigateToKeepServices = p export const navigateToComputeNodes = push(Routes.COMPUTE_NODES); -export const navigateToApiClientAuthorizations = push(Routes.API_CLIENT_AUTHORIZATIONS); +export const navigateToUsers = push(Routes.USERS); ++ ++export const navigateToApiClientAuthorizations = push(Routes.API_CLIENT_AUTHORIZATIONS); diff --cc src/store/project-panel/project-panel-middleware-service.ts index 257fc044,f2cc8a92..58f15877 --- a/src/store/project-panel/project-panel-middleware-service.ts +++ b/src/store/project-panel/project-panel-middleware-service.ts @@@ -22,11 -22,11 +22,11 @@@ import { Dispatch, MiddlewareAPI } fro import { ProjectResource } from "~/models/project"; import { updateResources } from "~/store/resources/resources-actions"; import { getProperty } from "~/store/properties/properties"; -import { snackbarActions, SnackbarKind } from '../snackbar/snackbar-actions'; +import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions'; import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions.ts'; -import { DataExplorer, getDataExplorer } from '../data-explorer/data-explorer-reducer'; +import { DataExplorer, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer'; - import { ListResults } from '~/services/common-service/common-resource-service'; + import { ListResults } from '~/services/common-service/common-service'; -import { loadContainers } from '../processes/processes-actions'; +import { loadContainers } from '~/store/processes/processes-actions'; import { ResourceKind } from '~/models/resource'; import { getResource } from "~/store/resources/resources"; import { CollectionResource } from "~/models/collection"; diff --cc src/store/store.ts index 1862d6f0,eef04750..2b0ada81 --- a/src/store/store.ts +++ b/src/store/store.ts @@@ -46,9 -46,8 +46,10 @@@ import { resourcesDataReducer } from "~ import { virtualMachinesReducer } from "~/store/virtual-machines/virtual-machines-reducer"; import { repositoriesReducer } from '~/store/repositories/repositories-reducer'; import { keepServicesReducer } from '~/store/keep-services/keep-services-reducer'; +import { UserMiddlewareService } from '~/store/users/user-panel-middleware-service'; +import { USERS_PANEL_ID } from '~/store/users/users-actions'; import { computeNodesReducer } from '~/store/compute-nodes/compute-nodes-reducer'; + import { apiClientAuthorizationsReducer } from '~/store/api-client-authorizations/api-client-authorizations-reducer'; const composeEnhancers = (process.env.NODE_ENV === 'development' && diff --cc src/store/users/user-panel-middleware-service.ts index 2f602093,00000000..bc4bb130 mode 100644,000000..100644 --- a/src/store/users/user-panel-middleware-service.ts +++ b/src/store/users/user-panel-middleware-service.ts @@@ -1,78 -1,0 +1,78 @@@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { ServiceRepository } from '~/services/services'; +import { MiddlewareAPI, Dispatch } from 'redux'; +import { DataExplorerMiddlewareService, dataExplorerToListParams, listResultsToDataExplorerItemsMeta } from '~/store/data-explorer/data-explorer-middleware-service'; +import { RootState } from '~/store/store'; +import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions'; +import { DataExplorer, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer'; +import { updateResources } from '~/store/resources/resources-actions'; +import { FilterBuilder } from '~/services/api/filter-builder'; +import { SortDirection } from '~/components/data-table/data-column'; +import { OrderDirection, OrderBuilder } from '~/services/api/order-builder'; - import { ListResults } from '~/services/common-service/common-resource-service'; ++import { ListResults } from '~/services/common-service/common-service'; +import { userBindedActions } from '~/store/users/users-actions'; +import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer"; +import { UserResource } from '~/models/user'; +import { UserPanelColumnNames } from '~/views/user-panel/user-panel'; + +export class UserMiddlewareService extends DataExplorerMiddlewareService { + constructor(private services: ServiceRepository, id: string) { + super(id); + } + + async requestItems(api: MiddlewareAPI) { + const state = api.getState(); + const dataExplorer = getDataExplorer(state.dataExplorer, this.getId()); + try { + const response = await this.services.userService.list(getParams(dataExplorer)); + api.dispatch(updateResources(response.items)); + api.dispatch(setItems(response)); + } catch { + api.dispatch(couldNotFetchUsers()); + } + } +} + +export const getParams = (dataExplorer: DataExplorer) => ({ + ...dataExplorerToListParams(dataExplorer), + order: getOrder(dataExplorer), + filters: getFilters(dataExplorer) +}); + +export const getFilters = (dataExplorer: DataExplorer) => { + const filters = new FilterBuilder() + .addILike("username", dataExplorer.searchValue) + .getFilters(); + return filters; +}; + +export const getOrder = (dataExplorer: DataExplorer) => { + const sortColumn = getSortColumn(dataExplorer); + const order = new OrderBuilder(); + if (sortColumn) { + const sortDirection = sortColumn && sortColumn.sortDirection === SortDirection.ASC + ? OrderDirection.ASC + : OrderDirection.DESC; + const columnName = sortColumn && sortColumn.name === UserPanelColumnNames.LAST_NAME ? "lastName" : "firstName"; + return order + .addOrder(sortDirection, columnName) + .getOrder(); + } else { + return order.getOrder(); + } +}; + +export const setItems = (listResults: ListResults) => + userBindedActions.SET_ITEMS({ + ...listResultsToDataExplorerItemsMeta(listResults), + items: listResults.items.map(resource => resource.uuid), + }); + +const couldNotFetchUsers = () => + snackbarActions.OPEN_SNACKBAR({ + message: 'Could not fetch users.', + kind: SnackbarKind.ERROR + }); diff --cc src/store/workbench/workbench-actions.ts index d33bdafb,fdc91f43..bc5eac6c --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@@ -58,9 -58,8 +58,10 @@@ import { searchResultsPanelColumns } fr 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 { loadUsersPanel, userBindedActions } from '~/store/users/users-actions'; +import { userPanelColumns } from '~/views/user-panel/user-panel'; import { loadComputeNodesPanel } from '~/store/compute-nodes/compute-nodes-actions'; + import { loadApiClientAuthorizationsPanel } from '~/store/api-client-authorizations/api-client-authorizations-actions'; export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen'; diff --cc src/store/workflow-panel/workflow-middleware-service.ts index 000e9f5c,2cd910bd..6dbcf6ac --- a/src/store/workflow-panel/workflow-middleware-service.ts +++ b/src/store/workflow-panel/workflow-middleware-service.ts @@@ -14,8 -14,8 +14,8 @@@ import { SortDirection } from '~/compon import { WorkflowPanelColumnNames } from '~/views/workflow-panel/workflow-panel-view'; import { OrderDirection, OrderBuilder } from '~/services/api/order-builder'; import { WorkflowResource } from '~/models/workflow'; - import { ListResults } from '~/services/common-service/common-resource-service'; + import { ListResults } from '~/services/common-service/common-service'; -import { workflowPanelActions } from './workflow-panel-actions'; +import { workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions'; import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer"; export class WorkflowMiddlewareService extends DataExplorerMiddlewareService { diff --cc src/views-components/main-app-bar/account-menu.tsx index 412f8497,415cba37..44b113df --- a/src/views-components/main-app-bar/account-menu.tsx +++ b/src/views-components/main-app-bar/account-menu.tsx @@@ -12,9 -12,11 +12,12 @@@ 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, navigateToKeepServices, navigateToComputeNodes, navigateToMyAccount } from '~/store/navigation/navigation-action'; + import { + navigateToSshKeys, navigateToKeepServices, navigateToComputeNodes, + navigateToApiClientAuthorizations, navigateToMyAccount + } from '~/store/navigation/navigation-action'; import { openVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions"; +import { navigateToUsers } from '~/store/navigation/navigation-action'; interface AccountMenuProps { user?: User; @@@ -38,7 -40,7 +41,8 @@@ export const AccountMenu = connect(mapS dispatch(openRepositoriesPanel())}>Repositories dispatch(openCurrentTokenDialog)}>Current token dispatch(navigateToSshKeys)}>Ssh Keys + dispatch(navigateToUsers)}>Users + { user.isAdmin && dispatch(navigateToApiClientAuthorizations)}>Api Tokens } { user.isAdmin && dispatch(navigateToKeepServices)}>Keep Services } { user.isAdmin && dispatch(navigateToComputeNodes)}>Compute Nodes } dispatch(navigateToMyAccount)}>My account diff --cc src/views-components/main-content-bar/main-content-bar.tsx index 7022fc5e,a3279e37..8c2e4ced --- a/src/views-components/main-content-bar/main-content-bar.tsx +++ b/src/views-components/main-content-bar/main-content-bar.tsx @@@ -20,8 -20,8 +20,8 @@@ const isButtonVisible = ({ router }: Ro const pathname = router.location ? router.location.pathname : ''; return !Routes.matchWorkflowRoute(pathname) && !Routes.matchVirtualMachineRoute(pathname) && !Routes.matchRepositoriesRoute(pathname) && !Routes.matchSshKeysRoute(pathname) && - !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname) && - !Routes.matchUsersRoute(pathname); + !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname) && - !Routes.matchApiClientAuthorizationsRoute(pathname); ++ !Routes.matchApiClientAuthorizationsRoute(pathname) && !Routes.matchUsersRoute(pathname); }; export const MainContentBar = connect((state: RootState) => ({ diff --cc src/views/workbench/workbench.tsx index e0ec14f1,3cd040f7..70f2a2dd --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@@ -68,9 -71,7 +71,10 @@@ import { AttributesComputeNodeDialog } 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'; +import { UserPanel } from '~/views/user-panel/user-panel'; +import { UserAttributesDialog } from '~/views-components/user-dialog/attributes-dialog'; +import { CreateUserDialog } from '~/views-components/dialog-forms/create-user-dialog'; + import { HelpApiClientAuthorizationDialog } from '~/views-components/api-client-authorizations-dialog/help-dialog'; type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content'; @@@ -144,8 -145,8 +148,9 @@@ export const WorkbenchPanel + +