Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla@contractors.roche.com>
onValueClick?: () => void;
linkToUuid?: string;
copyValue?: string;
+ uuidEnhancer?: Function;
}
type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules> & FederationConfig & DispatchProp;
}
render() {
- const { label, link, value, children, classes, classLabel,
+ const { uuidEnhancer, label, link, value, children, classes, classLabel,
classValue, lowercaseValue, onValueClick, linkToUuid,
localCluster, remoteHostsConfig, sessions, copyValue } = this.props;
let valueNode: React.ReactNode;
if (linkToUuid) {
+ const uuid = uuidEnhancer ? uuidEnhancer(linkToUuid) : linkToUuid;
const linkUrl = getNavUrl(linkToUuid || "", { localCluster, remoteHostsConfig, sessions });
if (linkUrl[0] === '/') {
- valueNode = <Link to={linkUrl} className={classes.link}>{linkToUuid}</Link>;
+ valueNode = <Link to={linkUrl} className={classes.link}>{uuid}</Link>;
} else {
- valueNode = <a href={linkUrl} className={classes.link} target='_blank'>{linkToUuid}</a>;
+ valueNode = <a href={linkUrl} className={classes.link} target='_blank'>{uuid}</a>;
}
} else if (link) {
valueNode = <a href={link} className={classes.link} target='_blank'>{value}</a>;
} else {
valueNode = value;
}
+
return <Typography component="div" className={classes.attribute}>
<Typography component="div" className={classnames([classes.label, classLabel])}>{label}</Typography>
<Typography
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { unionize, ofType, UnionOf } from '~/common/unionize';
+import { extractUuidObjectType, ResourceObjectType } from '~/models/resource';
+import { ServiceRepository } from '~/services/services';
+import { RootState } from '../store';
+
+export type OwnerNameUuidEnhancerAction = UnionOf<typeof ownerNameUuidEnhancerActions>;
+
+export interface OwnerNameState {
+ name: string;
+ uuid: string;
+}
+
+export const ownerNameUuidEnhancerActions = unionize({
+ SET_OWNER_NAME_BY_UUID: ofType<OwnerNameState>()
+});
+
+export const fetchOwnerNameByUuid = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const objectType = extractUuidObjectType(uuid);
+
+ switch (objectType) {
+ case ResourceObjectType.USER:
+ services.userService.get(uuid, false)
+ .then((data) =>
+ dispatch(
+ ownerNameUuidEnhancerActions.SET_OWNER_NAME_BY_UUID({
+ uuid,
+ name: (data as any).fullName,
+ })
+ )
+ );
+ break;
+ case ResourceObjectType.GROUP:
+ services.groupsService.get(uuid, false)
+ .then((data) =>
+ dispatch(
+ ownerNameUuidEnhancerActions.SET_OWNER_NAME_BY_UUID({
+ uuid,
+ name: (data as any).name,
+ })
+ )
+ );
+ break;
+ default:
+ break;
+ }
+ };
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ownerNameUuidEnhancerActions, OwnerNameUuidEnhancerAction, OwnerNameState } from './owner-name-uuid-enhancer-actions';
+
+export const ownerNameUuidEnhancerReducer = (state = {}, action: OwnerNameUuidEnhancerAction) =>
+ ownerNameUuidEnhancerActions.match(action, {
+ SET_OWNER_NAME_BY_UUID: (data: OwnerNameState) => ({...state, [data.uuid]: data.name }),
+ default: () => state,
+ });
\ No newline at end of file
import { CollectionsWithSameContentAddressMiddlewareService } from '~/store/collections-content-address-panel/collections-content-address-middleware-service';
import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
import { ownerNameReducer } from '~/store/owner-name/owner-name-reducer';
+import { ownerNameUuidEnhancerReducer } from './owner-name-uuid-enhancer/owner-name-uuid-enhancer-reducer';
import { SubprocessMiddlewareService } from '~/store/subprocess-panel/subprocess-panel-middleware-service';
import { SUBPROCESS_PANEL_ID } from '~/store/subprocess-panel/subprocess-panel-actions';
import { ALL_PROCESSES_PANEL_ID } from './all-processes-panel/all-processes-panel-action';
dialog: dialogReducer,
favorites: favoritesReducer,
ownerName: ownerNameReducer,
+ ownerNameUuidEnhancer: ownerNameUuidEnhancerReducer,
publicFavorites: publicFavoritesReducer,
form: formReducer,
processLogsPanel: processLogsPanelReducer,
import { resourceLabel } from '~/common/labels';
import { DetailsData } from "./details-data";
import { DetailsAttribute } from "~/components/details-attribute/details-attribute";
+import OwnerNameUuidEnhancer from '../owner-name-uuid-enhancer/owner-name-uuid-enhancer';
export class ProcessDetails extends DetailsData<ProcessResource> {
getDetails() {
return <div>
<DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROCESS)} />
- <DetailsAttribute label='Owner' linkToUuid={this.item.ownerUuid} value={this.item.ownerUuid} />
+ <DetailsAttribute label='Owner' linkToUuid={this.item.ownerUuid} value={this.item.ownerUuid}
+ uuidEnhancer={(uuid: string) => <OwnerNameUuidEnhancer uuid={uuid} />} />
<DetailsAttribute label='Status' value={this.item.state} />
<DetailsAttribute label='Last modified' value={formatDate(this.item.modifiedAt)} />
import { ArvadosTheme } from '~/common/custom-theme';
import { Dispatch } from 'redux';
import { getPropertyChip } from '../resource-properties-form/property-chip';
+import OwnerNameUuidEnhancer from '../owner-name-uuid-enhancer/owner-name-uuid-enhancer';
export class ProjectDetails extends DetailsData<ProjectResource> {
getIcon(className?: string) {
withStyles(styles)(
({ classes, project, onClick }: ProjectDetailsComponentProps) => <div>
<DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROJECT)} />
- <DetailsAttribute label='Owner' linkToUuid={project.ownerUuid} lowercaseValue={true} />
+ <DetailsAttribute label='Owner' linkToUuid={project.ownerUuid}
+ uuidEnhancer={(uuid: string) => <OwnerNameUuidEnhancer uuid={uuid} />} lowercaseValue={true} />
<DetailsAttribute label='Last modified' value={formatDate(project.modifiedAt)} />
<DetailsAttribute label='Created at' value={formatDate(project.createdAt)} />
<DetailsAttribute label='Project UUID' linkToUuid={project.uuid} value={project.uuid} />
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { mount, configure } from 'enzyme';
+import * as Adapter from "enzyme-adapter-react-16";
+import { OwnerNameUuidEnhancer, OwnerNameUuidEnhancerProps } from './owner-name-uuid-enhancer';
+
+configure({ adapter: new Adapter() });
+
+describe('NotFoundPanelRoot', () => {
+ let props: OwnerNameUuidEnhancerProps;
+
+ beforeEach(() => {
+ props = {
+ ownerNamesMap: {},
+ fetchOwner: () => {},
+ uuid: 'zzzz-tpzed-xxxxxxxxxxxxxxx',
+ };
+ });
+
+ it('should render uuid without name', () => {
+ // when
+ const wrapper = mount(<OwnerNameUuidEnhancer {...props} />);
+
+ // then
+ expect(wrapper.html()).toBe('<span>zzzz-tpzed-xxxxxxxxxxxxxxx</span>');
+ });
+
+ it('should render uuid with name', () => {
+ // given
+ const fullName = 'John Doe';
+
+ // setup
+ props.ownerNamesMap = {
+ [props.uuid]: fullName
+ };
+
+ // when
+ const wrapper = mount(<OwnerNameUuidEnhancer {...props} />);
+
+ // then
+ expect(wrapper.html()).toBe('<span>zzzz-tpzed-xxxxxxxxxxxxxxx (John Doe)</span>');
+ });
+});
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { connect } from 'react-redux';
+import { fetchOwnerNameByUuid } from '~/store/owner-name-uuid-enhancer/owner-name-uuid-enhancer-actions';
+
+export interface OwnerNameUuidEnhancerOwnProps {
+ uuid: string;
+}
+
+export interface OwnerNameUuidEnhancerRootDataProps {
+ ownerNamesMap: any;
+}
+
+export interface OwnerNameUuidEnhancerDispatchProps {
+ fetchOwner: Function;
+}
+
+export type OwnerNameUuidEnhancerProps = OwnerNameUuidEnhancerOwnProps & OwnerNameUuidEnhancerRootDataProps & OwnerNameUuidEnhancerDispatchProps;
+
+export const OwnerNameUuidEnhancer = ({ uuid, ownerNamesMap, fetchOwner }: OwnerNameUuidEnhancerProps) => {
+ React.useEffect(() => {
+ if (!ownerNamesMap[uuid]) {
+ fetchOwner(uuid);
+ }
+ }, [uuid, ownerNamesMap, fetchOwner]);
+
+ return <span>{uuid}{ownerNamesMap[uuid] ? ` (${ownerNamesMap[uuid]})` : ''}</span>;
+};
+
+const mapStateToProps = (state: RootState): OwnerNameUuidEnhancerRootDataProps => {
+ return {
+ ownerNamesMap: state.ownerNameUuidEnhancer,
+ };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): OwnerNameUuidEnhancerDispatchProps => ({
+ fetchOwner: (uuid: string) => dispatch<any>(fetchOwnerNameByUuid(uuid))
+});
+
+export default connect<OwnerNameUuidEnhancerRootDataProps, OwnerNameUuidEnhancerDispatchProps, OwnerNameUuidEnhancerOwnProps>(mapStateToProps, mapDispatchToProps)
+ (OwnerNameUuidEnhancer);
import { getUserUuid } from '~/common/getuser';
import { getProgressIndicator } from '~/store/progress-indicator/progress-indicator-reducer';
import { COLLECTION_PANEL_LOAD_FILES, loadCollectionFiles, COLLECTION_PANEL_LOAD_FILES_THRESHOLD } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
+import OwnerNameUuidEnhancer from '../../views-components/owner-name-uuid-enhancer/owner-name-uuid-enhancer';
import { Link } from 'react-router-dom';
import { Link as ButtonLink } from '@material-ui/core';
<Grid item xs={12} md={mdSize}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
label={isOldVersion ? "This version's UUID" : "Collection UUID"}
- linkToUuid={item.uuid} />
+ linkToUuid={`${item.uuid} AAAAAAAAAAAAAAAPortable data hash`} />
</Grid>
<Grid item xs={12} md={mdSize}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
</Grid>
<Grid item xs={12} md={mdSize}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
- label='Owner' linkToUuid={item.ownerUuid} />
+ label='Owner' linkToUuid={item.ownerUuid}
+ uuidEnhancer={(uuid: string) => <OwnerNameUuidEnhancer uuid={uuid} />} />
</Grid>
<Grid item xs={12} md={mdSize}>
<DetailsAttribute classLabel={classes.label} classValue={classes.value}
<DetailsAttribute
classLabel={classes.label} classValue={classes.value}
label='Version number'
- value={ showVersionBrowser !== undefined
+ value={showVersionBrowser !== undefined
? <Tooltip title="Open version browser"><ButtonLink underline='none' className={classes.button} onClick={() => showVersionBrowser()}>
{<span data-cy='collection-version-number'>{item.version}</span>}
</ButtonLink></Tooltip>