-
Notifications
You must be signed in to change notification settings - Fork 2
Feat/events multiselect bulk actions #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
ec4350a
42287da
40ca14d
0593ed3
dd309a5
9a0fc08
7ed3481
d497afd
dd84f03
ace53d0
530adaa
c212292
049bace
f250de5
ccd4a7e
6535527
93fd728
94d4e84
f2ab338
81a682f
c271563
57582bd
1e13972
2e9fba0
0ee4684
c4963c4
bf64ade
ae8d3dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| /** | ||
| * Mongo ObjectId string of the public "Join Demo Workspace" (Garage landing). | ||
| * Keep in sync with operations that seed demo data in Mongo. | ||
| */ | ||
| export const DEMO_WORKSPACE_ID = '6213b6a01e6281087467cc7a'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -918,6 +918,111 @@ class EventsFactory extends Factory { | |
| return collection.updateOne(query, update); | ||
| } | ||
|
|
||
| /** | ||
| * Max original event ids per bulkToggleEventMark request | ||
| */ | ||
| static get BULK_TOGGLE_EVENT_MARK_MAX() { | ||
| return 100; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. specify exact problem |
||
|
|
||
| /** | ||
| * Bulk mark for resolved / ignored / starred (not the same as per-event toggleEventMark). | ||
| * - If every found event already has the mark: remove it from all (bulk "undo"). | ||
| * - Otherwise: set the mark on every found event that does not have it yet (never remove | ||
| * from a subset when the selection is mixed). | ||
| * Only 'resolved', 'ignored' and 'starred' are allowed for bulk. | ||
| * | ||
| * @param {string[]} eventIds - original event ids | ||
| * @param {string} mark - 'resolved' | 'ignored' | 'starred' | ||
| * @returns {Promise<{ updatedCount: number, updatedEventIds: string[], failedEventIds: string[] }>} | ||
| */ | ||
| async bulkToggleEventMark(eventIds, mark) { | ||
| if (mark !== 'resolved' && mark !== 'ignored' && mark !== 'starred') { | ||
| throw new Error(`bulkToggleEventMark: mark must be resolved, ignored or starred, got ${mark}`); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validation should be on resolver level (or via graphql schema) |
||
|
|
||
| const max = EventsFactory.BULK_TOGGLE_EVENT_MARK_MAX; | ||
| const unique = [ ...new Set((eventIds || []).map(id => String(id))) ]; | ||
|
|
||
| if (unique.length > max) { | ||
| throw new Error(`bulkToggleEventMark: at most ${max} event ids allowed`); | ||
| } | ||
|
|
||
| const failedEventIds = []; | ||
| const validObjectIds = []; | ||
|
|
||
| for (const id of unique) { | ||
| if (!ObjectId.isValid(id)) { | ||
| failedEventIds.push(id); | ||
| } else { | ||
| validObjectIds.push(new ObjectId(id)); | ||
| } | ||
| } | ||
|
|
||
| if (validObjectIds.length === 0) { | ||
| return { | ||
| updatedCount: 0, | ||
| updatedEventIds: [], | ||
| failedEventIds, | ||
| }; | ||
| } | ||
|
|
||
| const collection = this.getCollection(this.TYPES.EVENTS); | ||
| const found = await collection.find({ _id: { $in: validObjectIds } }).toArray(); | ||
| const foundByIdStr = new Map(found.map(doc => [doc._id.toString(), doc])); | ||
|
|
||
| for (const oid of validObjectIds) { | ||
| const idStr = oid.toString(); | ||
|
|
||
| if (!foundByIdStr.has(idStr)) { | ||
| failedEventIds.push(idStr); | ||
| } | ||
| } | ||
|
|
||
| const nowSec = Math.floor(Date.now() / 1000); | ||
| const markKey = `marks.${mark}`; | ||
| const allHaveMark = found.length > 0 && found.every(doc => doc.marks && doc.marks[mark]); | ||
| const ops = []; | ||
| const updatedEventIds = []; | ||
|
|
||
| for (const doc of found) { | ||
| const hasMark = doc.marks && doc.marks[mark]; | ||
| let update; | ||
|
|
||
| if (allHaveMark) { | ||
| update = { $unset: { [markKey]: '' } }; | ||
| } else if (!hasMark) { | ||
| update = { $set: { [markKey]: nowSec } }; | ||
| } else { | ||
| continue; | ||
| } | ||
|
|
||
| ops.push({ | ||
| updateOne: { | ||
| filter: { _id: doc._id }, | ||
| update, | ||
| }, | ||
| }); | ||
| updatedEventIds.push(doc._id.toString()); | ||
| } | ||
|
|
||
| if (ops.length === 0) { | ||
| return { | ||
| updatedCount: 0, | ||
| updatedEventIds: [], | ||
| failedEventIds, | ||
| }; | ||
| } | ||
|
|
||
|
neSpecc marked this conversation as resolved.
|
||
| const bulkResult = await collection.bulkWrite(ops, { ordered: false }); | ||
|
|
||
| return { | ||
| updatedCount: bulkResult.modifiedCount + bulkResult.upsertedCount, | ||
| updatedEventIds, | ||
| failedEventIds, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Remove all project events | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -452,6 +452,26 @@ type RemoveAssigneeResponse { | |||||||
| success: Boolean! | ||||||||
| } | ||||||||
|
|
||||||||
| """ | ||||||||
| Result of bulk toggling event marks (resolve / ignore) | ||||||||
|
Dobrunia marked this conversation as resolved.
Outdated
|
||||||||
| """ | ||||||||
| type BulkToggleEventMarksResult { | ||||||||
| """ | ||||||||
| Number of events updated in the database | ||||||||
| """ | ||||||||
| updatedCount: Int! | ||||||||
|
|
||||||||
| """ | ||||||||
| Original event ids actually toggled in this operation | ||||||||
| """ | ||||||||
| updatedEventIds: [ID!]! | ||||||||
|
|
||||||||
| """ | ||||||||
| Event ids that were not updated (invalid id or not found) | ||||||||
| """ | ||||||||
| failedEventIds: [ID!]! | ||||||||
| } | ||||||||
|
|
||||||||
| type EventsMutations { | ||||||||
| """ | ||||||||
| Set an assignee for the selected event | ||||||||
|
|
@@ -504,6 +524,28 @@ extend type Mutation { | |||||||
| mark: EventMark! | ||||||||
| ): Boolean! | ||||||||
|
|
||||||||
| """ | ||||||||
| Toggle the same mark on many original events at once (resolved, ignored or starred). | ||||||||
| Same toggle semantics as toggleEventMark per event. | ||||||||
|
||||||||
| Same toggle semantics as toggleEventMark per event. | |
| Uses bulk semantics: if every selected event already has the mark, clear it for all; | |
| otherwise set it on each selected event that does not have it yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unexpected change