From 09c61f87d52388ebfe97f478d536f4f194755401 Mon Sep 17 00:00:00 2001 From: Daniel Kos Date: Tue, 19 Jun 2018 10:49:17 +0200 Subject: [PATCH] Decoupled services from redux Feature #13632 Arvados-DCO-1.1-Signed-off-by: Daniel Kos --- src/index.tsx | 6 +++--- src/services/auth-service/auth-service.ts | 15 ++++++++------- .../collection-service/collection-service.ts | 8 +------- src/services/project-service/project-service.ts | 7 +------ src/store/auth/auth-action.ts | 15 +++++++++++++-- src/store/auth/auth-reducer.test.ts | 11 +++++------ src/store/auth/auth-reducer.ts | 10 +--------- src/store/collection/collection-action.ts | 10 ++++++++++ src/store/project/project-action.ts | 10 ++++++++++ src/views-components/api-token/api-token.tsx | 9 +++++---- src/views/workbench/workbench.tsx | 5 ++--- 11 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 9e1f103c..cf1610f8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,7 +6,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Provider } from "react-redux"; import Workbench from './views/workbench/workbench'; -import ProjectList from './views-components/project-list/project-list'; import './index.css'; import { Route } from "react-router"; import createBrowserHistory from "history/createBrowserHistory"; @@ -14,7 +13,8 @@ import configureStore from "./store/store"; import { ConnectedRouter } from "react-router-redux"; import ApiToken from "./views-components/api-token/api-token"; import authActions from "./store/auth/auth-action"; -import { authService, projectService } from "./services/services"; +import { authService } from "./services/services"; +import { getProjectList } from "./store/project/project-action"; const history = createBrowserHistory(); @@ -31,7 +31,7 @@ const store = configureStore({ store.dispatch(authActions.INIT()); const rootUuid = authService.getRootUuid(); -store.dispatch(projectService.getProjectList(rootUuid)); +store.dispatch(getProjectList(rootUuid)); const App = () => diff --git a/src/services/auth-service/auth-service.ts b/src/services/auth-service/auth-service.ts index 5878dc6e..d71f0299 100644 --- a/src/services/auth-service/auth-service.ts +++ b/src/services/auth-service/auth-service.ts @@ -4,8 +4,6 @@ import { API_HOST, serverApi } from "../../common/api/server-api"; import { User } from "../../models/user"; -import { Dispatch } from "redux"; -import actions from "../../store/auth/auth-action"; export const API_TOKEN_KEY = 'apiToken'; export const USER_EMAIL_KEY = 'userEmail'; @@ -79,13 +77,16 @@ export default class AuthService { window.location.assign(`${API_HOST}/logout?return_to=${currentUrl}`); } - public getUserDetails = () => (dispatch: Dispatch): Promise => { - dispatch(actions.USER_DETAILS_REQUEST()); + public getUserDetails = (): Promise => { return serverApi .get('/users/current') - .then(resp => { - dispatch(actions.USER_DETAILS_SUCCESS(resp.data)); - }); + .then(resp => ({ + email: resp.data.email, + firstName: resp.data.first_name, + lastName: resp.data.last_name, + uuid: resp.data.uuid, + ownerUuid: resp.data.owner_uuid + })); } public getRootUuid() { diff --git a/src/services/collection-service/collection-service.ts b/src/services/collection-service/collection-service.ts index 77ea7ea3..171cd856 100644 --- a/src/services/collection-service/collection-service.ts +++ b/src/services/collection-service/collection-service.ts @@ -3,9 +3,6 @@ // SPDX-License-Identifier: AGPL-3.0 import { serverApi } from "../../common/api/server-api"; -import { Dispatch } from "redux"; -import actions from "../../store/collection/collection-action"; -import UrlBuilder from "../../common/api/url-builder"; import FilterBuilder, { FilterField } from "../../common/api/filter-builder"; import { ArvadosResource } from "../response"; import { Collection } from "../../models/collection"; @@ -31,8 +28,7 @@ interface CollectionsResponse { } export default class CollectionService { - public getCollectionList = (parentUuid?: string) => (dispatch: Dispatch): Promise => { - dispatch(actions.COLLECTIONS_REQUEST()); + public getCollectionList = (parentUuid?: string): Promise => { if (parentUuid) { const fb = new FilterBuilder(); fb.addLike(FilterField.OWNER_UUID, parentUuid); @@ -48,11 +44,9 @@ export default class CollectionService { ownerUuid: g.owner_uuid, kind: g.kind } as Collection)); - dispatch(actions.COLLECTIONS_SUCCESS({collections})); return collections; }); } else { - dispatch(actions.COLLECTIONS_SUCCESS({collections: []})); return Promise.resolve([]); } } diff --git a/src/services/project-service/project-service.ts b/src/services/project-service/project-service.ts index 8070a386..bc340818 100644 --- a/src/services/project-service/project-service.ts +++ b/src/services/project-service/project-service.ts @@ -4,9 +4,7 @@ import { serverApi } from "../../common/api/server-api"; import { Dispatch } from "redux"; -import actions from "../../store/project/project-action"; import { Project } from "../../models/project"; -import UrlBuilder from "../../common/api/url-builder"; import FilterBuilder, { FilterField } from "../../common/api/filter-builder"; import { ArvadosResource } from "../response"; @@ -27,8 +25,7 @@ interface GroupsResponse { } export default class ProjectService { - public getProjectList = (parentUuid?: string) => (dispatch: Dispatch): Promise => { - dispatch(actions.PROJECTS_REQUEST(parentUuid)); + public getProjectList = (parentUuid?: string): Promise => { if (parentUuid) { const fb = new FilterBuilder(); fb.addLike(FilterField.OWNER_UUID, parentUuid); @@ -44,11 +41,9 @@ export default class ProjectService { ownerUuid: g.owner_uuid, kind: g.kind } as Project)); - dispatch(actions.PROJECTS_SUCCESS({projects, parentItemId: parentUuid})); return projects; }); } else { - dispatch(actions.PROJECTS_SUCCESS({projects: [], parentItemId: parentUuid})); return Promise.resolve([]); } } diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts index e18c78b1..a6e6f797 100644 --- a/src/store/auth/auth-action.ts +++ b/src/store/auth/auth-action.ts @@ -3,7 +3,9 @@ // SPDX-License-Identifier: AGPL-3.0 import { ofType, default as unionize, UnionOf } from "unionize"; -import { UserDetailsResponse } from "../../services/auth-service/auth-service"; +import { Dispatch } from "redux"; +import { authService } from "../../services/services"; +import { User } from "../../models/user"; const actions = unionize({ SAVE_API_TOKEN: ofType(), @@ -11,11 +13,20 @@ const actions = unionize({ LOGOUT: {}, INIT: {}, USER_DETAILS_REQUEST: {}, - USER_DETAILS_SUCCESS: ofType() + USER_DETAILS_SUCCESS: ofType() }, { tag: 'type', value: 'payload' }); +export const getUserDetails = () => (dispatch: Dispatch): Promise => { + dispatch(actions.USER_DETAILS_REQUEST()); + return authService.getUserDetails().then(details => { + dispatch(actions.USER_DETAILS_SUCCESS(details)); + return details; + }); +}; + + export type AuthAction = UnionOf; export default actions; diff --git a/src/store/auth/auth-reducer.test.ts b/src/store/auth/auth-reducer.test.ts index a60e82a6..2e7c1a24 100644 --- a/src/store/auth/auth-reducer.test.ts +++ b/src/store/auth/auth-reducer.test.ts @@ -68,16 +68,15 @@ describe('auth-reducer', () => { it('should set user details on success fetch', () => { const initialState = undefined; - const userDetails = { + const user = { email: "test@test.com", - first_name: "John", - last_name: "Doe", + firstName: "John", + lastName: "Doe", uuid: "uuid", - owner_uuid: "ownerUuid", - is_admin: true + ownerUuid: "ownerUuid" }; - const state = authReducer(initialState, actions.USER_DETAILS_SUCCESS(userDetails)); + const state = authReducer(initialState, actions.USER_DETAILS_SUCCESS(user)); expect(state).toEqual({ apiToken: undefined, user: { diff --git a/src/store/auth/auth-reducer.ts b/src/store/auth/auth-reducer.ts index 02b9d30c..f6974fd2 100644 --- a/src/store/auth/auth-reducer.ts +++ b/src/store/auth/auth-reducer.ts @@ -6,7 +6,6 @@ import actions, { AuthAction } from "./auth-action"; import { User } from "../../models/user"; import { authService } from "../../services/services"; import { removeServerApiAuthorizationHeader, setServerApiAuthorizationHeader } from "../../common/api/server-api"; -import { UserDetailsResponse } from "../../services/auth-service/auth-service"; export interface AuthState { user?: User; @@ -39,14 +38,7 @@ const authReducer = (state: AuthState = {}, action: AuthAction) => { authService.logout(); return {...state, apiToken: undefined}; }, - USER_DETAILS_SUCCESS: (ud: UserDetailsResponse) => { - const user = { - email: ud.email, - firstName: ud.first_name, - lastName: ud.last_name, - uuid: ud.uuid, - ownerUuid: ud.owner_uuid - }; + USER_DETAILS_SUCCESS: (user: User) => { authService.saveUser(user); return {...state, user}; }, diff --git a/src/store/collection/collection-action.ts b/src/store/collection/collection-action.ts index 5f1d60f3..f50e6458 100644 --- a/src/store/collection/collection-action.ts +++ b/src/store/collection/collection-action.ts @@ -4,6 +4,8 @@ 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(), @@ -15,5 +17,13 @@ const actions = unionize({ value: 'payload' }); +export const getCollectionList = (parentUuid?: string) => (dispatch: Dispatch): Promise => { + dispatch(actions.COLLECTIONS_REQUEST()); + return collectionService.getCollectionList(parentUuid).then(collections => { + dispatch(actions.COLLECTIONS_SUCCESS({collections})); + return collections; + }); +}; + export type CollectionAction = UnionOf; export default actions; diff --git a/src/store/project/project-action.ts b/src/store/project/project-action.ts index 2856de66..728b1cc9 100644 --- a/src/store/project/project-action.ts +++ b/src/store/project/project-action.ts @@ -4,6 +4,8 @@ import { Project } from "../../models/project"; import { default as unionize, ofType, UnionOf } from "unionize"; +import { projectService } from "../../services/services"; +import { Dispatch } from "redux"; const actions = unionize({ CREATE_PROJECT: ofType(), @@ -16,5 +18,13 @@ const actions = unionize({ value: 'payload' }); +export const getProjectList = (parentUuid?: string) => (dispatch: Dispatch): Promise => { + dispatch(actions.PROJECTS_REQUEST()); + return projectService.getProjectList(parentUuid).then(projects => { + dispatch(actions.PROJECTS_SUCCESS({projects, parentItemId: parentUuid})); + return projects; + }); +}; + export type ProjectAction = UnionOf; export default actions; diff --git a/src/views-components/api-token/api-token.tsx b/src/views-components/api-token/api-token.tsx index 7656bf87..e4ba4914 100644 --- a/src/views-components/api-token/api-token.tsx +++ b/src/views-components/api-token/api-token.tsx @@ -5,8 +5,9 @@ import { Redirect, RouteProps } from "react-router"; import * as React from "react"; import { connect, DispatchProp } from "react-redux"; -import authActions from "../../store/auth/auth-action"; -import { authService, projectService } from "../../services/services"; +import authActions, { getUserDetails } from "../../store/auth/auth-action"; +import { authService } from "../../services/services"; +import { getProjectList } from "../../store/project/project-action"; interface ApiTokenProps { } @@ -23,9 +24,9 @@ class ApiToken extends React.Component(authService.getUserDetails()).then(() => { + this.props.dispatch(getUserDetails()).then(() => { const rootUuid = authService.getRootUuid(); - this.props.dispatch(projectService.getProjectList(rootUuid)); + this.props.dispatch(getProjectList(rootUuid)); }); } render() { diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 5da3968d..bf05dadc 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -14,11 +14,10 @@ import { RootState } from "../../store/store"; import MainAppBar, { MainAppBarActionProps, MainAppBarMenuItem } from '../../views-components/main-app-bar/main-app-bar'; import { Breadcrumb } from '../../components/breadcrumbs/breadcrumbs'; import { push } from 'react-router-redux'; -import projectActions from "../../store/project/project-action"; +import projectActions, { getProjectList } from "../../store/project/project-action"; import ProjectTree from '../../views-components/project-tree/project-tree'; import { TreeItem, TreeItemStatus } from "../../components/tree/tree"; import { Project } from "../../models/project"; -import { projectService } from '../../services/services'; import { getTreePath } from '../../store/project/project-reducer'; import DataExplorer from '../data-explorer/data-explorer'; @@ -133,7 +132,7 @@ class Workbench extends React.Component { if (status === TreeItemStatus.Loaded) { this.openProjectItem(itemId); } else { - this.props.dispatch(projectService.getProjectList(itemId)) + this.props.dispatch(getProjectList(itemId)) .then(() => this.openProjectItem(itemId)); } } -- 2.30.2