[Gekidou] Groups + group-memberships deferred fetch (#6370)

* WIP

* Actions updated to fetch remote first, and local on error

* Groups fetch and save

* PR Feedback: prepare vs store and undefined fix

* Forgot to add file

* Groups Mention WIP

* Groups highlight!

* Merge, PR Feedback

* PR Feedback

* PR Feedback: Try/Catch blocks

* PR Feedback

* Rebased with PR feedback

* Exclusion fix, plus id order

* Tidies up iterations

* Loops updated

* Update app/database/operator/server_data_operator/handlers/group.ts

Co-authored-by: Avinash Lingaloo <avinashlng1080@gmail.com>

* PR Feedback: Remove unnecessary prepare/store methods

* Newline ESLint error

* Extracts out id generation for group-associations

* Batches if not fetchOnly

Co-authored-by: Avinash Lingaloo <avinashlng1080@gmail.com>
This commit is contained in:
Shaz MJ
2022-07-07 20:20:06 +10:00
committed by GitHub
parent b2d838d3da
commit dcfc6e7927
12 changed files with 158 additions and 59 deletions

View File

@@ -2,16 +2,21 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import {transformGroupRecord} from '@database/operator/server_data_operator/transformers/group';
import {transformGroupMembershipRecord, transformGroupRecord} from '@database/operator/server_data_operator/transformers/group';
import {getUniqueRawsBy} from '@database/operator/utils/general';
import {queryGroupMembershipForMember} from '@queries/servers/group';
import {generateGroupAssociationId} from '@utils/groups';
import {logWarning} from '@utils/log';
import type {HandleGroupArgs} from '@typings/database/database';
import type {HandleGroupArgs, HandleGroupMembershipForMemberArgs} from '@typings/database/database';
import type GroupModel from '@typings/database/models/servers/group';
import type GroupMembershipModel from '@typings/database/models/servers/group_membership';
const {GROUP, GROUP_MEMBERSHIP} = MM_TABLES.SERVER;
const {GROUP} = MM_TABLES.SERVER;
export interface GroupHandlerMix {
handleGroups: ({groups, prepareRecordsOnly}: HandleGroupArgs) => Promise<GroupModel[]>;
handleGroupMembershipsForMember: ({userId, groups, prepareRecordsOnly}: HandleGroupMembershipForMemberArgs) => Promise<GroupMembershipModel[]>;
}
const GroupHandler = (superclass: any) => class extends superclass implements GroupHandlerMix {
@@ -41,6 +46,66 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
prepareRecordsOnly,
});
};
/**
* handleGroupMembershipsForMember: Handler responsible for the Create/Update operations occurring on the GroupMembership table from the 'Server' schema
* @param {string} userId
* @param {HandleGroupMembershipForMemberArgs} groupMembershipsArgs
* @param {GroupMembership[]} groupMembershipsArgs.groupMemberships
* @param {boolean} groupMembershipsArgs.prepareRecordsOnly
* @throws DataOperatorException
* @returns {Promise<GroupMembershipModel[]>}
*/
handleGroupMembershipsForMember = async ({userId, groups, prepareRecordsOnly = true}: HandleGroupMembershipForMemberArgs): Promise<GroupMembershipModel[]> => {
// Get existing group memberships
const existingGroupMemberships = await queryGroupMembershipForMember(this.database, userId).fetch();
let records: GroupMembershipModel[] = [];
let rawValues: GroupMembership[] = [];
// Nothing to add or remove
if (!groups?.length && !existingGroupMemberships.length) {
return records;
} else if (!groups?.length && existingGroupMemberships.length) { // No groups - remove all existing ones
records = existingGroupMemberships.map((gm) => gm.prepareDestroyPermanently());
} else if (groups?.length && !existingGroupMemberships.length) { // No existing groups - add all new ones
rawValues = groups.map((g) => ({id: generateGroupAssociationId(g.id, userId), user_id: userId, group_id: g.id}));
} else if (groups?.length && existingGroupMemberships.length) { // If both, we only want to save new ones and delete one's no longer in groups
const groupsSet: {[key: string]: GroupMembership} = {};
for (const g of groups) {
groupsSet[`${g.id}`] = {id: generateGroupAssociationId(g.id, userId), user_id: userId, group_id: g.id};
}
for (const gm of existingGroupMemberships) {
// Check if existingGroups overlaps with groups
if (groupsSet[gm.groupId]) {
// If there is an existing group already, we don't need to add it
delete groupsSet[gm.groupId];
} else {
// No group? Remove existing one
records.push(gm.prepareDestroyPermanently());
}
}
rawValues.push(...Object.values(groupsSet));
}
records.push(...(await this.handleRecords({
fieldName: 'id',
transformer: transformGroupMembershipRecord,
rawValues,
tableName: GROUP_MEMBERSHIP,
prepareRecordsOnly: true,
})));
// Batch update if there are records
if (records.length && !prepareRecordsOnly) {
await this.batchRecords(records);
}
return records;
};
};
export default GroupHandler;