-
Notifications
You must be signed in to change notification settings - Fork 324
/
changeTaskTeam.ts
130 lines (123 loc) · 4.64 KB
/
changeTaskTeam.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import {GraphQLID, GraphQLNonNull} from 'graphql'
import {SubscriptionChannel} from 'parabol-client/types/constEnums'
import removeEntityKeepText from 'parabol-client/utils/draftjs/removeEntityKeepText'
import getRethink from '../../database/rethinkDriver'
import generateUID from '../../generateUID'
import {getUserId, isTeamMember} from '../../utils/authorization'
import publish from '../../utils/publish'
import standardError from '../../utils/standardError'
import ChangeTaskTeamPayload from '../types/ChangeTaskTeamPayload'
export default {
type: ChangeTaskTeamPayload,
description: 'Change the team a task is associated with',
args: {
taskId: {
type: new GraphQLNonNull(GraphQLID),
description: 'The task to change'
},
teamId: {
type: new GraphQLNonNull(GraphQLID),
description: 'The new team to assign the task to'
}
},
async resolve(_source, {taskId, teamId}, {authToken, dataLoader, socketId: mutatorId}) {
const r = await getRethink()
const now = new Date()
const operationId = dataLoader.share()
const subOptions = {mutatorId, operationId}
// AUTH
const viewerId = getUserId(authToken)
if (!isTeamMember(authToken, teamId)) {
return standardError(new Error('Team not found'), {userId: viewerId})
}
const task = await r
.table('Task')
.get(taskId)
.run()
if (!task) {
return standardError(new Error('Task not found'), {userId: viewerId})
}
const {content, tags, teamId: oldTeamId} = task
if (!isTeamMember(authToken, oldTeamId)) {
return standardError(new Error('Team not found'), {userId: viewerId})
}
if (task.userId !== viewerId) {
return standardError(new Error('Cannot change team for a task assigned to someone else'), {
userId: viewerId
})
}
// RESOLUTION
// filter mentions of old team members from task content
const [oldTeamMembers, newTeamMembers] = await dataLoader
.get('teamMembersByTeamId')
.loadMany([oldTeamId, teamId])
const oldTeamUserIds = oldTeamMembers.map(({userId}) => userId)
const newTeamUserIds = newTeamMembers.map(({userId}) => userId)
const userIdsOnlyOnOldTeam = oldTeamUserIds.filter((oldTeamUserId) => {
return !newTeamUserIds.find((newTeamUserId) => newTeamUserId === oldTeamUserId)
})
const rawContent = JSON.parse(content)
const eqFn = (entity) =>
entity.type === 'MENTION' &&
Boolean(userIdsOnlyOnOldTeam.find((userId) => userId === entity.data.userId))
const {rawContent: nextRawContent} = removeEntityKeepText(rawContent, eqFn)
const updates = {
content: rawContent === nextRawContent ? undefined : JSON.stringify(nextRawContent),
updatedAt: now,
teamId
}
// If there is a task with the same integration hash in the new team, then delete it first.
// This is done so there are no duplicates and also solves the issue of the conflicting task being
// private or archived.
const {deletedConflictingIntegrationTask} = await r({
deletedConflictingIntegrationTask:
task.integrationHash &&
r
.table('Task')
.getAll(task.integrationHash, {index: 'integrationHash'})
.filter({teamId})
.delete({returnChanges: true}),
newTask: r
.table('Task')
.get(taskId)
.update(updates),
taskHistory: r
.table('TaskHistory')
.between([taskId, r.minval], [taskId, r.maxval], {
index: 'taskIdUpdatedAt'
})
.orderBy({index: 'taskIdUpdatedAt'})
.nth(-1)
.default(null)
.do((taskHistoryRecord) => {
// prepopulated cards will not have a history
return r.branch(
taskHistoryRecord.ne(null),
r.table('TaskHistory').insert(taskHistoryRecord.merge(updates, {id: generateUID()})),
null
)
})
}).run()
const deletedTasks = (deletedConflictingIntegrationTask as any)?.changes?.map(
({old_val}) => old_val
)
deletedTasks?.forEach((task) => {
const isPrivate = task.tags.includes('private')
const data = {task}
newTeamMembers.forEach(({userId}) => {
if (!isPrivate || userId === task.userId) {
publish(SubscriptionChannel.TASK, userId, 'DeleteTaskPayload', data, subOptions)
}
})
})
const isPrivate = tags.includes('private')
const data = {taskId}
const teamMembers = oldTeamMembers.concat(newTeamMembers)
teamMembers.forEach(({userId}) => {
if (!isPrivate || userId === task.userId) {
publish(SubscriptionChannel.TASK, userId, 'ChangeTaskTeamPayload', data, subOptions)
}
})
return data
}
}