Refactor common/team

This commit is contained in:
Jerko Steiner 2019-09-11 13:34:41 +07:00
parent ae2ed21f05
commit 25589b870c
31 changed files with 106 additions and 133 deletions

View File

@ -1,4 +1,4 @@
import { IAPIDef, IUserInTeam, team as Team, user as User } from '@rondo.dev/common'
import { IAPIDef, IUserInTeam, ITeamService, TeamServiceMethods, UserServiceMethods, TeamActions, UserActions, IUserService, ITeamUsers, Team } from '@rondo.dev/common'
import { HTTPClientMock } from '@rondo.dev/http-client'
import { getError } from '@rondo.dev/test-utils'
import React from 'react'
@ -18,11 +18,11 @@ describe('TeamConnector', () => {
}
const [teamClient, teamClientMock] =
createClientMock<Team.ITeamService>(Team.TeamServiceMethods)
createClientMock<ITeamService>(TeamServiceMethods)
const [userClient, userClientMock] =
createClientMock<User.IUserService>(User.UserServiceMethods)
let teamActions!: Team.TeamActions
let userActions!: User.UserActions
createClientMock<IUserService>(UserServiceMethods)
let teamActions!: TeamActions
let userActions!: UserActions
beforeEach(() => {
teamClientMock.find.mockResolvedValue(teams)
teamClientMock.findUsers.mockResolvedValue(users)
@ -48,7 +48,7 @@ describe('TeamConnector', () => {
</MemoryRouter>,
)
const teams: Team.Team[] = [{
const teams: Team[] = [{
id: 100,
name: 'my-team',
userId: 1,
@ -57,7 +57,7 @@ describe('TeamConnector', () => {
userTeams: [],
}]
const users: Team.ITeamUsers = {
const users: ITeamUsers = {
teamId: 123,
usersInTeam: [{
teamId: 123,
@ -84,7 +84,7 @@ describe('TeamConnector', () => {
})
it('creates a new team', async () => {
const newTeam: Team.Team = {
const newTeam: Team = {
id: 101,
name: 'new-team',
createDate: '',

View File

@ -1,12 +1,11 @@
import { team, user } from '@rondo.dev/common'
import { TReduxed } from '@rondo.dev/jsonrpc'
import { TeamActions, UserActions } from '@rondo.dev/common'
import { bindActionCreators, pack, TStateSelector } from '@rondo.dev/redux'
import { TeamManager } from './TeamManager'
import { ITeamState } from './TeamReducer'
export function configure<State>(
teamActions: team.TeamActions,
userActions: user.UserActions,
teamActions: TeamActions,
userActions: UserActions,
getLocalState: TStateSelector<State, ITeamState>,
) {
const Component = pack(

View File

@ -1,15 +1,15 @@
import { team as Team } from '@rondo.dev/common'
import { Button, Control, Heading, Help, Input } from 'bloomer'
import React from 'react'
import { FaCheck, FaEdit, FaPlusSquare } from 'react-icons/fa'
import { TeamActions, Team } from '@rondo.dev/common'
export type TTeamEditorProps = {
type: 'add'
onAddTeam: Team.TeamActions['create']
onAddTeam: TeamActions['create']
} | {
type: 'update'
onUpdateTeam: Team.TeamActions['update']
team: Team.Team
onUpdateTeam: TeamActions['update']
team: Team
}
export interface ITeamEditorState {
@ -27,7 +27,7 @@ extends React.PureComponent<TTeamEditorProps, ITeamEditorState> {
name: props.type === 'update' ? this.getName(props.team) : '',
}
}
getName(team?: Team.Team) {
getName(team?: Team) {
return team ? team.name : ''
}
componentWillReceiveProps(nextProps: TTeamEditorProps) {

View File

@ -1,21 +1,21 @@
import { team as Team, TReadonlyRecord } from '@rondo.dev/common'
import { TReadonlyRecord, Team, TeamActions } from '@rondo.dev/common'
import { Button, Panel, PanelBlock, PanelHeading } from 'bloomer'
import React from 'react'
import { FaEdit, FaPlus, FaTimes } from 'react-icons/fa'
import { Link } from 'react-router-dom'
export interface ITeamListProps {
ListButtons?: React.ComponentType<{team: Team.Team}>
teamsById: TReadonlyRecord<number, Team.Team>
ListButtons?: React.ComponentType<{team: Team}>
teamsById: TReadonlyRecord<number, Team>
teamIds: ReadonlyArray<number>
onAddTeam: Team.TeamActions['create']
onRemoveTeam: Team.TeamActions['remove']
onAddTeam: TeamActions['create']
onRemoveTeam: TeamActions['remove']
}
export interface ITeamProps {
ListButtons?: React.ComponentType<{team: Team.Team}>
team: Team.Team
onRemoveTeam: Team.TeamActions['remove']
ListButtons?: React.ComponentType<{team: Team}>
team: Team
onRemoveTeam: TeamActions['remove']
}
export class TeamRow extends React.PureComponent<ITeamProps> {

View File

@ -1,4 +1,4 @@
import { IUserInTeam, team as Team, TReadonlyRecord, user as User } from '@rondo.dev/common'
import { IUserInTeam, TReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
import { Panel, PanelBlock, PanelHeading } from 'bloomer'
import { History, Location } from 'history'
import React from 'react'
@ -13,12 +13,12 @@ export interface ITeamManagerProps {
location: Location
match: Match<any>
ListButtons?: React.ComponentType<{team: Team.Team}>
ListButtons?: React.ComponentType<{team: Team}>
teamActions: Team.TeamActions
findUserByEmail: User.UserActions['findUserByEmail']
teamActions: TeamActions
findUserByEmail: UserActions['findUserByEmail']
teamsById: TReadonlyRecord<number, Team.Team>
teamsById: TReadonlyRecord<number, Team>
teamIds: ReadonlyArray<number>
userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>

View File

@ -1,4 +1,4 @@
import { indexBy, ITeam, IUserInTeam, team as T, TReadonlyRecord, without } from '@rondo.dev/common'
import { indexBy, Team as _Team, IUserInTeam, TReadonlyRecord, without, TeamActions } from '@rondo.dev/common'
import { createReducer } from '@rondo.dev/jsonrpc'
export interface ITeamState {
@ -6,7 +6,7 @@ export interface ITeamState {
readonly error: string
readonly teamIds: ReadonlyArray<number>
readonly teamsById: TReadonlyRecord<number, ITeam>
readonly teamsById: TReadonlyRecord<number, _Team>
readonly userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
readonly usersByKey: TReadonlyRecord<string, IUserInTeam>
@ -28,7 +28,7 @@ function getUserKey(userInTeam: {userId: number, teamId: number}) {
}
export const Team = createReducer('teamService', defaultState)
.withMapping<T.TeamActions>({
.withMapping<TeamActions>({
create(state, action) {
return {
teamIds: state.teamIds.indexOf(action.payload.id) >= 0

View File

@ -1,4 +1,4 @@
import { IUser, IUserInTeam, team as Team, TReadonlyRecord, user as User } from '@rondo.dev/common'
import { IUser, IUserInTeam, TReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
import { Button, Control, Heading, Help, Input, Panel, PanelBlock, PanelHeading } from 'bloomer'
import React from 'react'
import { FaCheck, FaTimes, FaUser } from 'react-icons/fa'
@ -7,13 +7,13 @@ const EMPTY_ARRAY: ReadonlyArray<string> = []
export interface ITeamUsersProps {
// fetchMyTeams: () => void,
fetchUsersInTeam: Team.TeamActions['findUsers']
findUserByEmail: User.UserActions['findUserByEmail']
fetchUsersInTeam: TeamActions['findUsers']
findUserByEmail: UserActions['findUserByEmail']
onAddUser: Team.TeamActions['addUser']
onRemoveUser: Team.TeamActions['removeUser']
onAddUser: TeamActions['addUser']
onRemoveUser: TeamActions['removeUser']
team: Team.Team
team: Team
userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
usersByKey: TReadonlyRecord<string, IUserInTeam>
}
@ -25,8 +25,8 @@ export interface ITeamUserProps {
}
export interface IAddUserProps {
onAddUser: Team.TeamActions['addUser']
onSearchUser: User.UserActions['findUserByEmail']
onAddUser: TeamActions['addUser']
onSearchUser: UserActions['findUserByEmail']
teamId: number
}

View File

@ -5,7 +5,7 @@
},
"references": [
{
"path": "../comments-common/tsconfig.esm.json"
"path": "../common/tsconfig.esm.json"
}
]
}

View File

@ -6,6 +6,6 @@
},
"references": [
{"path": "../redux"},
{"path": "../comments-common"}
{"path": "../common"}
]
}

View File

@ -1,6 +1,5 @@
import { ICredentials } from './ICredentials'
import { INewUser } from './INewUser'
import { IUser } from './IUser'
import { IUser, INewUser } from './user'
import { ICredentials } from './auth'
export interface IAPIDef {
'/auth/register': {

View File

@ -1,5 +0,0 @@
export interface ITeam {
readonly id: number
readonly name: string
readonly userId: number
}

View File

@ -1,8 +0,0 @@
import {ITeam} from './ITeam'
export interface IUserTeam {
userId: number
teamId: number
roleId: number
team?: ITeam
}

View File

@ -0,0 +1 @@
export * from './ICredentials'

View File

@ -1,26 +1,17 @@
export * from './entities'
export * from './IContext'
export * from './IAPIDef'
export * from './ICredentials'
export * from './auth'
export * from './ILogger'
export * from './INewUser'
export * from './IRole'
export * from './ITeam'
export * from './IUser'
export * from './IUser'
export * from './IUserInTeam'
export * from './IUserTeam'
export * from './StringUtils'
export * from './filterProps'
export * from './indexBy'
export * from './types'
export * from './without'
import * as team from './team'
export {team}
import * as user from './user'
export {user}
export * from './team'
export * from './user'
import * as entities from './entities'
export {entities}

View File

@ -0,0 +1,5 @@
export interface ITeamAddUserParams {
teamId: number
userId: number
roleId: number
}

View File

@ -0,0 +1,3 @@
export interface ITeamCreateParams {
name: string
}

View File

@ -0,0 +1,3 @@
export interface ITeamRemoveParams {
id: number
}

View File

@ -1,39 +1,13 @@
// import {ITeam} from './ITeam'
import { TReduxed } from '@rondo.dev/jsonrpc'
import { keys } from 'ts-transformer-keys'
import { Team } from './entities'
import { Team } from '../entities'
import { ITeamAddUserParams } from './ITeamAddUserParams'
import { ITeamCreateParams } from './ITeamCreateParams'
import { ITeamRemoveParams } from './ITeamRemoveParams'
import { ITeamUpdateParams } from './ITeamUpdateParams'
import { ITeamUsers } from './ITeamUsers'
import { IUserInTeam } from './IUserInTeam'
export { Team }
export interface ITeamAddUserParams {
teamId: number
userId: number
roleId: number
}
export interface ITeamCreateParams {
name: string
}
export interface ITeamRemoveParams {
id: number
}
export interface ITeamUpdateParams {
id: number
name: string
}
export interface ITeamUsers {
teamId: number
usersInTeam: IUserInTeam[]
}
export interface IContext {
userId: number
}
export interface ITeamService {
create(params: ITeamCreateParams): Promise<Team>
@ -50,8 +24,6 @@ export interface ITeamService {
find(): Promise<Team[]>
findUsers(teamId: number): Promise<ITeamUsers>
// TODO add other methods
}
export const TeamServiceMethods = keys<ITeamService>()

View File

@ -0,0 +1,4 @@
export interface ITeamUpdateParams {
id: number
name: string
}

View File

@ -0,0 +1,6 @@
import { IUserInTeam } from './IUserInTeam'
export interface ITeamUsers {
teamId: number
usersInTeam: IUserInTeam[]
}

View File

@ -0,0 +1,7 @@
export * from './ITeamAddUserParams'
export * from './ITeamCreateParams'
export * from './ITeamRemoveParams'
export * from './ITeamService'
export * from './ITeamUpdateParams'
export * from './ITeamUsers'
export * from './IUserInTeam'

View File

@ -1,4 +1,4 @@
import {ICredentials} from './ICredentials'
import {ICredentials} from '../auth/ICredentials'
export interface INewUser extends ICredentials {
firstName: string

View File

@ -1,18 +1,7 @@
import { TReduxed } from '@rondo.dev/jsonrpc'
import { keys } from 'ts-transformer-keys'
import { ICredentials } from './ICredentials'
import { IUser } from './IUser'
export interface IChangePasswordParams {
oldPassword: string
newPassword: string
}
export interface ICreateUserParams extends ICredentials {
firstName: string
lastName: string
}
export interface IUserService {
getProfile(): Promise<IUser>
// TODO exposing search by email might be a security concern

View File

@ -0,0 +1,3 @@
export * from './INewUser'
export * from './IUserService'
export * from './IUser'

View File

@ -1,4 +1,4 @@
import { team as Team } from '@rondo.dev/common'
import { TeamServiceMethods, ITeamService } from '@rondo.dev/common'
import { test } from '../test'
describe('team', () => {
@ -15,9 +15,9 @@ describe('team', () => {
})
const getClient = () =>
test.rpc<Team.ITeamService>(
test.rpc<ITeamService>(
'/rpc/teamService',
Team.TeamServiceMethods,
TeamServiceMethods,
headers,
)

View File

@ -6,19 +6,23 @@ import {IUserPermissions} from '../services/IUserPermissions'
import {
trim,
entities as e,
team as t,
IUserInTeam,
ITeamService,
ITeamCreateParams,
ITeamRemoveParams,
ITeamUpdateParams,
ITeamAddUserParams,
} from '@rondo.dev/common'
import { ensureLoggedIn, IContext, RPC } from './RPC'
import { IUserInTeam } from '@rondo.dev/common/lib/team/IUserInTeam'
@ensureLoggedIn
export class TeamService implements RPC<t.ITeamService> {
export class TeamService implements RPC<ITeamService> {
constructor(
protected readonly db: IDatabase,
protected readonly permissions: IUserPermissions,
) {}
async create(context: IContext, params: t.ITeamCreateParams) {
async create(context: IContext, params: ITeamCreateParams) {
const userId = context.user!.id
const name = trim(params.name)
@ -42,7 +46,7 @@ export class TeamService implements RPC<t.ITeamService> {
return (await this.findOne(context, team.id))!
}
async remove(context: IContext, {id}: t.ITeamRemoveParams) {
async remove(context: IContext, {id}: ITeamRemoveParams) {
const userId = context.user!.id
await this.permissions.belongsToTeam({
@ -59,7 +63,7 @@ export class TeamService implements RPC<t.ITeamService> {
return {id}
}
async update(context: IContext, {id, name}: t.ITeamUpdateParams) {
async update(context: IContext, {id, name}: ITeamUpdateParams) {
const userId = context.user!.id
await this.permissions.belongsToTeam({
@ -77,7 +81,7 @@ export class TeamService implements RPC<t.ITeamService> {
return (await this.findOne(context, id))!
}
async addUser(context: IContext, params: t.ITeamAddUserParams) {
async addUser(context: IContext, params: ITeamAddUserParams) {
const {userId, teamId, roleId} = params
await this.db.getRepository(UserTeam)
.save({userId, teamId, roleId})
@ -93,7 +97,7 @@ export class TeamService implements RPC<t.ITeamService> {
return this._mapUserInTeam(userTeam!)
}
async removeUser(context: IContext, params: t.ITeamAddUserParams) {
async removeUser(context: IContext, params: ITeamAddUserParams) {
const {teamId, userId, roleId} = params
await this.permissions.belongsToTeam({

View File

@ -1,5 +1,5 @@
import {test} from '../test'
import {user} from '@rondo.dev/common'
import { IUserService, UserServiceMethods } from '@rondo.dev/common'
describe('user', () => {
@ -12,9 +12,9 @@ describe('user', () => {
})
const createService = () => {
return test.rpc<user.IUserService>(
return test.rpc<IUserService>(
'/rpc/userService',
user.UserServiceMethods,
UserServiceMethods,
headers,
)
}

View File

@ -1,4 +1,4 @@
import { user as u } from '@rondo.dev/common'
import { IUserService } from '@rondo.dev/common'
import { compare, hash } from 'bcrypt'
import createError from 'http-errors'
import { IDatabase } from '../database/IDatabase'
@ -10,7 +10,7 @@ const SALT_ROUNDS = 10
const MIN_PASSWORD_LENGTH = 10
@ensureLoggedIn
export class UserService implements RPC<u.IUserService> {
export class UserService implements RPC<IUserService> {
constructor(protected readonly db: IDatabase) {}
async getProfile(context: IContext) {