[ARVADOS-WORKBENCH2] created: 1.1.4-280-g0ce21e3

Git user git at public.curoverse.com
Thu Jul 12 08:34:32 EDT 2018


        at  0ce21e3487078a53f1b653446b5e615cd6dda709 (commit)


commit 0ce21e3487078a53f1b653446b5e615cd6dda709
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Thu Jul 12 14:34:21 2018 +0200

    Unify use of Collection and Project resource types
    
    Feature #13798
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/models/collection.ts b/src/models/collection.ts
index cf5b4e6..8effd8e 100644
--- a/src/models/collection.ts
+++ b/src/models/collection.ts
@@ -2,13 +2,9 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { Resource as R } from "./resource";
 import { Resource } from "../common/api/common-resource-service";
 import { ResourceKind } from "./kinds";
 
-export interface Collection extends R {
-}
-
 export interface CollectionResource extends Resource {
     kind: ResourceKind.Collection;
     name: string;
diff --git a/src/models/project.ts b/src/models/project.ts
index c44c8cc..1373ca7 100644
--- a/src/models/project.ts
+++ b/src/models/project.ts
@@ -2,12 +2,8 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { Resource as R } from "./resource";
 import { GroupResource, GroupClass } from "./group";
 
-export interface Project extends R {
-}
-
 export interface ProjectResource extends GroupResource {
     groupClass: GroupClass.Project;
 }
diff --git a/src/models/test-utils.ts b/src/models/test-utils.ts
new file mode 100644
index 0000000..aa8d96b
--- /dev/null
+++ b/src/models/test-utils.ts
@@ -0,0 +1,50 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { GroupClass, GroupResource } from "./group";
+import { ResourceKind } from "./kinds";
+import { Resource } from "../common/api/common-resource-service";
+
+type ResourceUnion = GroupResource;
+
+export const mockResource = (kind: ResourceKind, data: Partial<Exclude<ResourceUnion, "kind">>) => {
+    switch (kind) {
+        case ResourceKind.Group:
+            return mockGroupResource({ ...data, kind });
+        default:
+            return mockCommonResource({ ...data, kind });
+    }
+};
+
+export const mockGroupResource = (data: Partial<Exclude<GroupResource, "kind">>): GroupResource => ({
+    createdAt: "",
+    deleteAt: "",
+    description: "",
+    etag: "",
+    groupClass: null,
+    href: "",
+    isTrashed: false,
+    kind: ResourceKind.Group,
+    modifiedAt: "",
+    modifiedByClientUuid: "",
+    modifiedByUserUuid: "",
+    name: "",
+    ownerUuid: "",
+    properties: "",
+    trashAt: "",
+    uuid: "",
+    writeableBy: []
+});
+
+const mockCommonResource = <T extends Resource>(data: Partial<T> & { kind: ResourceKind }): Resource => ({
+    createdAt: "",
+    etag: "",
+    href: "",
+    kind: data.kind,
+    modifiedAt: "",
+    modifiedByClientUuid: "",
+    modifiedByUserUuid: "",
+    ownerUuid: "",
+    uuid: ""
+});
diff --git a/src/services/collection-service/collection-service.ts b/src/services/collection-service/collection-service.ts
deleted file mode 100644
index 8412dea..0000000
--- a/src/services/collection-service/collection-service.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { serverApi } from "../../common/api/server-api";
-import FilterBuilder from "../../common/api/filter-builder";
-import { ArvadosResource } from "../response";
-import { Collection } from "../../models/collection";
-import { getResourceKind } from "../../models/resource";
-
-interface CollectionResource extends ArvadosResource {
-    name: string;
-    description: string;
-    properties: any;
-    portable_data_hash: string;
-    manifest_text: string;
-    replication_desired: number;
-    replication_confirmed: number;
-    replication_confirmed_at: string;
-    trash_at: string;
-    delete_at: string;
-    is_trashed: boolean;
-}
-
-interface CollectionsResponse {
-    offset: number;
-    limit: number;
-    items: CollectionResource[];
-}
-
-export default class CollectionService {
-    public getCollectionList = (parentUuid?: string): Promise<Collection[]> => {
-        if (parentUuid) {
-            const fb = new FilterBuilder();
-            fb.addLike("ownerUuid", parentUuid);
-            return serverApi.get<CollectionsResponse>('/collections', { params: {
-                filters: fb.serialize()
-            }}).then(resp => {
-                const collections = resp.data.items.map(g => ({
-                    name: g.name,
-                    createdAt: g.created_at,
-                    modifiedAt: g.modified_at,
-                    href: g.href,
-                    uuid: g.uuid,
-                    ownerUuid: g.owner_uuid,
-                    kind: getResourceKind(g.kind)
-                } as Collection));
-                return collections;
-            });
-        } else {
-            return Promise.resolve([]);
-        }
-    }
-}
diff --git a/src/services/services.ts b/src/services/services.ts
index 1e9a74d..143e97b 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -3,12 +3,10 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import AuthService from "./auth-service/auth-service";
-import CollectionService from "./collection-service/collection-service";
 import GroupsService from "./groups-service/groups-service";
 import { serverApi } from "../common/api/server-api";
 import ProjectService from "./project-service/project-service";
 
 export const authService = new AuthService();
-export const collectionService = new CollectionService();
 export const groupsService = new GroupsService(serverApi);
 export const projectService = new ProjectService(serverApi);
diff --git a/src/store/collection/collection-action.ts b/src/store/collection/collection-action.ts
deleted file mode 100644
index f50e645..0000000
--- a/src/store/collection/collection-action.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { Collection } from "../../models/collection";
-import { default as unionize, ofType, UnionOf } from "unionize";
-import { Dispatch } from "redux";
-import { collectionService } from "../../services/services";
-
-const actions = unionize({
-    CREATE_COLLECTION: ofType<Collection>(),
-    REMOVE_COLLECTION: ofType<string>(),
-    COLLECTIONS_REQUEST: ofType<any>(),
-    COLLECTIONS_SUCCESS: ofType<{ collections: Collection[] }>(),
-}, {
-    tag: 'type',
-    value: 'payload'
-});
-
-export const getCollectionList = (parentUuid?: string) => (dispatch: Dispatch): Promise<Collection[]> => {
-    dispatch(actions.COLLECTIONS_REQUEST());
-    return collectionService.getCollectionList(parentUuid).then(collections => {
-        dispatch(actions.COLLECTIONS_SUCCESS({collections}));
-        return collections;
-    });
-};
-
-export type CollectionAction = UnionOf<typeof actions>;
-export default actions;
diff --git a/src/store/collection/collection-reducer.test.ts b/src/store/collection/collection-reducer.test.ts
deleted file mode 100644
index 7bc1aec..0000000
--- a/src/store/collection/collection-reducer.test.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import collectionsReducer from "./collection-reducer";
-import actions from "./collection-action";
-import { ResourceKind } from "../../models/resource";
-
-describe('collection-reducer', () => {
-    it('should add new collection to the list', () => {
-        const initialState = undefined;
-        const collection = {
-            name: 'test',
-            href: 'href',
-            createdAt: '2018-01-01',
-            modifiedAt: '2018-01-01',
-            ownerUuid: 'owner-test123',
-            uuid: 'test123',
-            kind: ResourceKind.COLLECTION
-        };
-
-        const state = collectionsReducer(initialState, actions.CREATE_COLLECTION(collection));
-        expect(state).toEqual([collection]);
-    });
-
-    it('should load collections', () => {
-        const initialState = undefined;
-        const collection = {
-            name: 'test',
-            href: 'href',
-            createdAt: '2018-01-01',
-            modifiedAt: '2018-01-01',
-            ownerUuid: 'owner-test123',
-            uuid: 'test123',
-            kind: ResourceKind.COLLECTION
-        };
-
-        const collections = [collection, collection];
-        const state = collectionsReducer(initialState, actions.COLLECTIONS_SUCCESS({ collections }));
-        expect(state).toEqual([collection, collection]);
-    });
-});
diff --git a/src/store/collection/collection-reducer.ts b/src/store/collection/collection-reducer.ts
deleted file mode 100644
index 5c257ea..0000000
--- a/src/store/collection/collection-reducer.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import actions, { CollectionAction } from "./collection-action";
-import { Collection } from "../../models/collection";
-
-export type CollectionState = Collection[];
-
-
-const collectionsReducer = (state: CollectionState = [], action: CollectionAction) => {
-    return actions.match(action, {
-        CREATE_COLLECTION: collection => [...state, collection],
-        REMOVE_COLLECTION: () => state,
-        COLLECTIONS_REQUEST: () => {
-            return [];
-        },
-        COLLECTIONS_SUCCESS: ({ collections }) => {
-            return collections;
-        },
-        default: () => state
-    });
-};
-
-export default collectionsReducer;
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index 7ba245a..25cc003 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -7,20 +7,17 @@ import projectActions, { getProjectList } from "../project/project-action";
 import { push } from "react-router-redux";
 import { TreeItemStatus } from "../../components/tree/tree";
 import { findTreeItem } from "../project/project-reducer";
-import { Resource, ResourceKind as R } from "../../models/resource";
-import sidePanelActions from "../side-panel/side-panel-action";
 import dataExplorerActions from "../data-explorer/data-explorer-action";
 import { PROJECT_PANEL_ID } from "../../views/project-panel/project-panel";
 import { RootState } from "../store";
-import { sidePanelData } from "../side-panel/side-panel-reducer";
-import { loadDetails } from "../details-panel/details-panel-action";
+import { Resource } from "../../common/api/common-resource-service";
 import { ResourceKind } from "../../models/kinds";
 
-export const getResourceUrl = (resource: Resource): string => {
+export const getResourceUrl = <T extends Resource>(resource: T): string => {
     switch (resource.kind) {
-        case R.PROJECT: return `/projects/${resource.uuid}`;
-        case R.COLLECTION: return `/collections/${resource.uuid}`;
-        default: return "";
+        case ResourceKind.Project: return `/projects/${resource.uuid}`;
+        case ResourceKind.Collection: return `/collections/${resource.uuid}`;
+        default: return resource.href;
     }
 };
 
@@ -32,7 +29,7 @@ export enum ItemMode {
 
 export const setProjectItem = (itemId: string, itemMode: ItemMode) =>
     (dispatch: Dispatch, getState: () => RootState) => {
-        const { projects, router, sidePanel } = getState();
+        const { projects, router } = getState();
         const treeItem = findTreeItem(projects.items, itemId);
 
         if (treeItem) {
@@ -41,7 +38,7 @@ export const setProjectItem = (itemId: string, itemMode: ItemMode) =>
                 dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(treeItem.data.uuid));
             }
 
-            const resourceUrl = getResourceUrl({ ...treeItem.data });
+            const resourceUrl = getResourceUrl(treeItem.data);
 
             if (itemMode === ItemMode.ACTIVE || itemMode === ItemMode.BOTH) {
                 if (router.location && !router.location.pathname.includes(resourceUrl)) {
diff --git a/src/store/project/project-action.ts b/src/store/project/project-action.ts
index c1a002f..3acf091 100644
--- a/src/store/project/project-action.ts
+++ b/src/store/project/project-action.ts
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 import { default as unionize, ofType, UnionOf } from "unionize";
 
-import { Project, ProjectResource } from "../../models/project";
+import { ProjectResource } from "../../models/project";
 import { projectService } from "../../services/services";
 import { Dispatch } from "redux";
 import { getResourceKind } from "../../models/resource";
@@ -19,7 +19,7 @@ const actions = unionize({
     CREATE_PROJECT_ERROR: ofType<string>(),
     REMOVE_PROJECT: ofType<string>(),
     PROJECTS_REQUEST: ofType<string>(),
-    PROJECTS_SUCCESS: ofType<{ projects: Project[], parentItemId?: string }>(),
+    PROJECTS_SUCCESS: ofType<{ projects: ProjectResource[], parentItemId?: string }>(),
     TOGGLE_PROJECT_TREE_ITEM_OPEN: ofType<string>(),
     TOGGLE_PROJECT_TREE_ITEM_ACTIVE: ofType<string>(),
     RESET_PROJECT_TREE_ACTIVITY: ofType<string>()
@@ -34,11 +34,7 @@ export const getProjectList = (parentUuid: string = '') => (dispatch: Dispatch)
         filters: FilterBuilder
             .create<ProjectResource>()
             .addEqual("ownerUuid", parentUuid)
-    }).then(listResults => {
-        const projects = listResults.items.map(item => ({
-            ...item,
-            kind: getResourceKind(item.kind)
-        }));
+    }).then(({ items: projects }) => {
         dispatch(actions.PROJECTS_SUCCESS({ projects, parentItemId: parentUuid }));
         return projects;
     });
diff --git a/src/store/project/project-reducer.test.ts b/src/store/project/project-reducer.test.ts
index f14ee9a..b2def81 100644
--- a/src/store/project/project-reducer.test.ts
+++ b/src/store/project/project-reducer.test.ts
@@ -5,21 +5,16 @@
 import projectsReducer, { getTreePath } from "./project-reducer";
 import actions from "./project-action";
 import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
-import { ResourceKind } from "../../models/resource";
+import { ResourceKind } from "../../models/kinds";
+import { mockResource } from "../../models/test-utils";
+import { ProjectResource } from "../../models/project";
+import { GroupClass } from "../../models/group";
 
 describe('project-reducer', () => {
 
     it('should load projects', () => {
         const initialState = undefined;
-        const project = {
-            name: 'test',
-            href: 'href',
-            createdAt: '2018-01-01',
-            modifiedAt: '2018-01-01',
-            ownerUuid: 'owner-test123',
-            uuid: 'test123',
-            kind: ResourceKind.PROJECT
-        };
+        const project = mockProject({ ownerUuid: "test123" });
 
         const projects = [project, project];
         const state = projectsReducer(initialState, actions.PROJECTS_SUCCESS({ projects, parentItemId: undefined }));
@@ -52,15 +47,7 @@ describe('project-reducer', () => {
     it('should remove activity on projects list', () => {
         const initialState = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT
-                },
+                data: mockProject(),
                 id: "1",
                 open: true,
                 active: true,
@@ -71,15 +58,7 @@ describe('project-reducer', () => {
         };
         const project = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT
-                },
+                data: { ...initialState.items[0] },
                 id: "1",
                 open: true,
                 active: false,
@@ -96,15 +75,7 @@ describe('project-reducer', () => {
     it('should toggle project tree item activity', () => {
         const initialState = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT
-                },
+                data: mockProject(),
                 id: "1",
                 open: true,
                 active: false,
@@ -115,15 +86,7 @@ describe('project-reducer', () => {
         };
         const project = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT,
-                },
+                data: { ...initialState.items[0] },
                 id: "1",
                 open: true,
                 active: true,
@@ -142,15 +105,7 @@ describe('project-reducer', () => {
     it('should close project tree item ', () => {
         const initialState = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT
-                },
+                data: mockProject(),
                 id: "1",
                 open: true,
                 active: false,
@@ -162,15 +117,7 @@ describe('project-reducer', () => {
         };
         const project = {
             items: [{
-                data: {
-                    name: 'test',
-                    href: 'href',
-                    createdAt: '2018-01-01',
-                    modifiedAt: '2018-01-01',
-                    ownerUuid: 'owner-test123',
-                    uuid: 'test123',
-                    kind: ResourceKind.PROJECT
-                },
+                data: { ...initialState.items[0] },
                 id: "1",
                 open: false,
                 active: false,
@@ -234,3 +181,5 @@ describe("findTreeBranch", () => {
     });
 
 });
+
+const mockProject = (data: Partial<ProjectResource> = {}) => mockResource(ResourceKind.Group, { ...data, groupClass: GroupClass.Project }) as ProjectResource;
diff --git a/src/store/project/project-reducer.ts b/src/store/project/project-reducer.ts
index 5693fb6..c008370 100644
--- a/src/store/project/project-reducer.ts
+++ b/src/store/project/project-reducer.ts
@@ -4,12 +4,12 @@
 
 import * as _ from "lodash";
 
-import { Project } from "../../models/project";
 import actions, { ProjectAction } from "./project-action";
 import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
+import { ProjectResource } from "../../models/project";
 
 export type ProjectState = {
-    items: Array<TreeItem<Project>>,
+    items: Array<TreeItem<ProjectResource>>,
     currentItemId: string,
     creator: ProjectCreator
 };
@@ -67,7 +67,7 @@ function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
     }
 }
 
-function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[], parentItemId?: string): Array<TreeItem<Project>> {
+function updateProjectTree(tree: Array<TreeItem<ProjectResource>>, projects: ProjectResource[], parentItemId?: string): Array<TreeItem<ProjectResource>> {
     let treeItem;
     if (parentItemId) {
         treeItem = findTreeItem(tree, parentItemId);
@@ -82,7 +82,7 @@ function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[],
         status: TreeItemStatus.Initial,
         data: p,
         items: []
-    } as TreeItem<Project>));
+    } as TreeItem<ProjectResource>));
 
     if (treeItem) {
         treeItem.items = items;
diff --git a/src/store/store.ts b/src/store/store.ts
index f74b877..36f9203 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -11,7 +11,6 @@ import projectsReducer, { ProjectState } from "./project/project-reducer";
 import sidePanelReducer, { SidePanelState } from './side-panel/side-panel-reducer';
 import authReducer, { AuthState } from "./auth/auth-reducer";
 import dataExplorerReducer, { DataExplorerState } from './data-explorer/data-explorer-reducer';
-import collectionsReducer, { CollectionState } from "./collection/collection-reducer";
 import { projectPanelMiddleware } from '../store/project-panel/project-panel-middleware';
 import detailsPanelReducer, { DetailsPanelState } from './details-panel/details-panel-reducer';
 
@@ -23,7 +22,6 @@ const composeEnhancers =
 export interface RootState {
     auth: AuthState;
     projects: ProjectState;
-    collections: CollectionState;
     router: RouterState;
     dataExplorer: DataExplorerState;
     sidePanel: SidePanelState;
@@ -33,7 +31,6 @@ export interface RootState {
 const rootReducer = combineReducers({
     auth: authReducer,
     projects: projectsReducer,
-    collections: collectionsReducer,
     router: routerReducer,
     dataExplorer: dataExplorerReducer,
     sidePanel: sidePanelReducer,
diff --git a/src/views-components/project-tree/project-tree.tsx b/src/views-components/project-tree/project-tree.tsx
index 511cbbb..17592a7 100644
--- a/src/views-components/project-tree/project-tree.tsx
+++ b/src/views-components/project-tree/project-tree.tsx
@@ -10,13 +10,13 @@ import ListItemIcon from '@material-ui/core/ListItemIcon';
 import Typography from '@material-ui/core/Typography';
 
 import Tree, { TreeItem, TreeItemStatus } from '../../components/tree/tree';
-import { Project } from '../../models/project';
+import { ProjectResource } from '../../models/project';
 
 export interface ProjectTreeProps {
-    projects: Array<TreeItem<Project>>;
+    projects: Array<TreeItem<ProjectResource>>;
     toggleOpen: (id: string, status: TreeItemStatus) => void;
     toggleActive: (id: string, status: TreeItemStatus) => void;
-    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<Project>) => void;
+    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectResource>) => void;
 }
 
 class ProjectTree<T> extends React.Component<ProjectTreeProps & WithStyles<CssRules>> {
@@ -29,7 +29,7 @@ class ProjectTree<T> extends React.Component<ProjectTreeProps & WithStyles<CssRu
                     onContextMenu={onContextMenu}
                     toggleItemOpen={toggleOpen}
                     toggleItemActive={toggleActive}
-                    render={(project: TreeItem<Project>) =>
+                    render={(project: TreeItem<ProjectResource>) =>
                         <span className={row}>
                             <ListItemIcon className={project.active ? active : ''}>
                                 <i className="fas fa-folder" />
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index e7a8ae3..75cc336 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -18,7 +18,6 @@ import { Breadcrumb } from '../../components/breadcrumbs/breadcrumbs';
 import { push } from 'react-router-redux';
 import ProjectTree from '../../views-components/project-tree/project-tree';
 import { TreeItem } from "../../components/tree/tree";
-import { Project } from "../../models/project";
 import { getTreePath } from '../../store/project/project-reducer';
 import sidePanelActions from '../../store/side-panel/side-panel-action';
 import SidePanel, { SidePanelItem } from '../../components/side-panel/side-panel';
@@ -35,6 +34,7 @@ import { authService } from '../../services/services';
 import detailsPanelActions, { loadDetails } from "../../store/details-panel/details-panel-action";
 import { ResourceKind } from '../../models/kinds';
 import { SidePanelIdentifiers } from '../../store/side-panel/side-panel-reducer';
+import { ProjectResource } from '../../models/project';
 
 const drawerWidth = 240;
 const appBarHeight = 100;
@@ -78,7 +78,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 });
 
 interface WorkbenchDataProps {
-    projects: Array<TreeItem<Project>>;
+    projects: Array<TreeItem<ProjectResource>>;
     currentProjectId: string;
     user?: User;
     sidePanelItems: SidePanelItem[];

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list