//
// 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;
//
// 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;
}
--- /dev/null
+// 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: ""
+});
+++ /dev/null
-// 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([]);
- }
- }
-}
// 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);
+++ /dev/null
-// 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;
+++ /dev/null
-// 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]);
- });
-});
+++ /dev/null
-// 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;
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;
}
};
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) {
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)) {
// 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";
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>()
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;
});
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 }));
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,
};
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,
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,
};
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,
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,
};
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,
});
});
+
+const mockProject = (data: Partial<ProjectResource> = {}) => mockResource(ResourceKind.Group, { ...data, groupClass: GroupClass.Project }) as ProjectResource;
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
};
}
}
-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);
status: TreeItemStatus.Initial,
data: p,
items: []
- } as TreeItem<Project>));
+ } as TreeItem<ProjectResource>));
if (treeItem) {
treeItem.items = items;
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';
export interface RootState {
auth: AuthState;
projects: ProjectState;
- collections: CollectionState;
router: RouterState;
dataExplorer: DataExplorerState;
sidePanel: SidePanelState;
const rootReducer = combineReducers({
auth: authReducer,
projects: projectsReducer,
- collections: collectionsReducer,
router: routerReducer,
dataExplorer: dataExplorerReducer,
sidePanel: sidePanelReducer,
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>> {
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" />
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';
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;
});
interface WorkbenchDataProps {
- projects: Array<TreeItem<Project>>;
+ projects: Array<TreeItem<ProjectResource>>;
currentProjectId: string;
user?: User;
sidePanelItems: SidePanelItem[];