[Gekidou] operator handlers improvements (#6136)

* Allow database operator handlers to deal with empty or undefined input values

* Prevent known handler warnings

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

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

* feedback review

* remove unnecessary !

Co-authored-by: Avinash Lingaloo <avinashlng1080@gmail.com>
This commit is contained in:
Elias Nahum
2022-04-07 09:17:57 -04:00
committed by GitHub
parent 3326f34933
commit 0950dbd21b
41 changed files with 334 additions and 369 deletions

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {
transformCategoryChannelRecord,
transformCategoryRecord,
@@ -36,10 +35,12 @@ const CategoryHandler = (superclass: any) => class extends superclass {
* @returns {Promise<CategoryModel[]>}
*/
handleCategories = async ({categories, prepareRecordsOnly = true}: HandleCategoryArgs): Promise<CategoryModel[]> => {
if (!categories.length) {
throw new DataOperatorException(
'An empty "categories" array has been passed to the handleCategories method',
if (!categories?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "categories" array has been passed to the handleCategories method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: categories, key: 'id'});
@@ -62,10 +63,13 @@ const CategoryHandler = (superclass: any) => class extends superclass {
* @returns {Promise<CategoryChannelModel[]>}
*/
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryModel[]> => {
if (!categoryChannels.length) {
throw new DataOperatorException(
'An empty "categoryChannels" array has been passed to the handleCategories method',
if (!categoryChannels?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "categoryChannels" array has been passed to the handleCategories method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: categoryChannels, key: 'id'});

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {
buildMyChannelKey,
buildChannelMembershipKey,
@@ -48,11 +47,13 @@ const ChannelHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<ChannelModel[]>}
*/
handleChannel = ({channels, prepareRecordsOnly = true}: HandleChannelArgs): Promise<ChannelModel[]> => {
if (!channels.length) {
throw new DataOperatorException(
'An empty "channels" array has been passed to the handleChannel method',
handleChannel = async ({channels, prepareRecordsOnly = true}: HandleChannelArgs): Promise<ChannelModel[]> => {
if (!channels?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "channels" array has been passed to the handleChannel method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: channels, key: 'id'});
@@ -74,11 +75,14 @@ const ChannelHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<MyChannelSettingsModel[]>}
*/
handleMyChannelSettings = ({settings, prepareRecordsOnly = true}: HandleMyChannelSettingsArgs): Promise<MyChannelSettingsModel[]> => {
if (!settings.length) {
throw new DataOperatorException(
'An empty "settings" array has been passed to the handleMyChannelSettings method',
handleMyChannelSettings = async ({settings, prepareRecordsOnly = true}: HandleMyChannelSettingsArgs): Promise<MyChannelSettingsModel[]> => {
if (!settings?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "settings" array has been passed to the handleMyChannelSettings method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: settings, key: 'id'});
@@ -101,11 +105,14 @@ const ChannelHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<ChannelInfoModel[]>}
*/
handleChannelInfo = ({channelInfos, prepareRecordsOnly = true}: HandleChannelInfoArgs): Promise<ChannelInfoModel[]> => {
if (!channelInfos.length) {
throw new DataOperatorException(
handleChannelInfo = async ({channelInfos, prepareRecordsOnly = true}: HandleChannelInfoArgs): Promise<ChannelInfoModel[]> => {
if (!channelInfos?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty "channelInfos" array has been passed to the handleMyChannelSettings method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({
@@ -130,11 +137,23 @@ const ChannelHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<MyChannelModel[]>}
*/
handleMyChannel = ({channels, myChannels, prepareRecordsOnly = true}: HandleMyChannelArgs): Promise<MyChannelModel[]> => {
if (!myChannels.length) {
throw new DataOperatorException(
'An empty "myChannels" array has been passed to the handleMyChannel method',
handleMyChannel = async ({channels, myChannels, prepareRecordsOnly = true}: HandleMyChannelArgs): Promise<MyChannelModel[]> => {
if (!myChannels?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "myChannels" array has been passed to the handleMyChannel method',
);
return [];
}
if (!channels?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "channels" array has been passed to the handleMyChannel method',
);
return [];
}
const channelMap = channels.reduce((result: Record<string, Channel>, channel) => {
@@ -173,11 +192,14 @@ const ChannelHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<ChannelMembershipModel[]>}
*/
handleChannelMembership = ({channelMemberships, prepareRecordsOnly = true}: HandleChannelMembershipArgs): Promise<ChannelMembershipModel[]> => {
if (!channelMemberships.length) {
throw new DataOperatorException(
handleChannelMembership = async ({channelMemberships, prepareRecordsOnly = true}: HandleChannelMembershipArgs): Promise<ChannelMembershipModel[]> => {
if (!channelMemberships?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty "channelMemberships" array has been passed to the handleChannelMembership method',
);
return [];
}
const memberships: ChannelMember[] = channelMemberships.map((m) => ({

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import DataOperatorException from '@database/exceptions/data_operator_exception';
import DatabaseManager from '@database/manager';
import {
transformCustomEmojiRecord,
@@ -115,6 +114,6 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
createOrUpdateRawValues: [{id: 'tos-1', value: '1'}],
prepareRecordsOnly: false,
}),
).rejects.toThrow(DataOperatorException);
).rejects.toThrow(Error);
});
});

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import BaseDataOperator from '@database/operator/base_data_operator';
import {
transformCustomEmojiRecord,
@@ -20,11 +19,13 @@ import type SystemModel from '@typings/database/models/servers/system';
const {SERVER: {CUSTOM_EMOJI, ROLE, SYSTEM}} = MM_TABLES;
export default class ServerDataOperatorBase extends BaseDataOperator {
handleRole = ({roles, prepareRecordsOnly = true}: HandleRoleArgs) => {
if (!roles.length) {
throw new DataOperatorException(
'An empty "values" array has been passed to the handleRole',
handleRole = async ({roles, prepareRecordsOnly = true}: HandleRoleArgs) => {
if (!roles?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "roles" array has been passed to the handleRole',
);
return [];
}
return this.handleRecords({
@@ -36,11 +37,13 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
}) as Promise<RoleModel[]>;
};
handleCustomEmojis = ({emojis, prepareRecordsOnly = true}: HandleCustomEmojiArgs) => {
if (!emojis.length) {
throw new DataOperatorException(
'An empty "values" array has been passed to the handleCustomEmojis',
handleCustomEmojis = async ({emojis, prepareRecordsOnly = true}: HandleCustomEmojiArgs) => {
if (!emojis?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "emojis" array has been passed to the handleCustomEmojis',
);
return [];
}
return this.handleRecords({
@@ -52,11 +55,13 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
}) as Promise<CustomEmojiModel[]>;
};
handleSystem = ({systems, prepareRecordsOnly = true}: HandleSystemArgs) => {
if (!systems.length) {
throw new DataOperatorException(
'An empty "values" array has been passed to the handleSystem',
handleSystem = async ({systems, prepareRecordsOnly = true}: HandleSystemArgs) => {
if (!systems?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "systems" array has been passed to the handleSystem',
);
return [];
}
return this.handleRecords({

View File

@@ -5,7 +5,6 @@ import {Q} from '@nozbe/watermelondb';
import {ActionType} from '@constants';
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {buildDraftKey} from '@database/operator/server_data_operator/comparators';
import {
transformDraftRecord,
@@ -48,11 +47,13 @@ const PostHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<DraftModel[]>}
*/
handleDraft = ({drafts, prepareRecordsOnly = true}: HandleDraftArgs): Promise<DraftModel[]> => {
if (!drafts.length) {
throw new DataOperatorException(
'An empty "drafts" array has been passed to the handleDraft method',
handleDraft = async ({drafts, prepareRecordsOnly = true}: HandleDraftArgs): Promise<DraftModel[]> => {
if (!drafts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "drafts" array has been passed to the handleDraft method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: drafts, key: 'channel_id'});
@@ -81,7 +82,11 @@ const PostHandler = (superclass: any) => class extends superclass {
const tableName = POST;
// We rely on the posts array; if it is empty, we stop processing
if (!posts.length) {
if (!posts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array has been passed to the handlePosts method',
);
return [];
}
@@ -229,7 +234,11 @@ const PostHandler = (superclass: any) => class extends superclass {
* @returns {Promise<FileModel[]>}
*/
handleFiles = async ({files, prepareRecordsOnly}: HandleFilesArgs): Promise<FileModel[]> => {
if (!files.length) {
if (!files?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "files" array has been passed to the handleFiles method',
);
return [];
}
@@ -264,6 +273,10 @@ const PostHandler = (superclass: any) => class extends superclass {
*/
handlePostsInThread = async (postsMap: Record<string, Post[]>, actionType: never, prepareRecordsOnly = false): Promise<PostsInThreadModel[]> => {
if (!postsMap || !Object.keys(postsMap).length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "postsMap" object has been passed to the handlePostsInThread method',
);
return [];
}
switch (actionType) {
@@ -291,6 +304,10 @@ const PostHandler = (superclass: any) => class extends superclass {
const permittedActions = Object.values(ActionType.POSTS);
if (!posts.length || !permittedActions.includes(actionType)) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array or an non-supported actionType has been passed to the handlePostsInChannel method',
);
return [];
}

View File

@@ -56,8 +56,12 @@ const PostsInChannelHandler = (superclass: any) => class extends superclass {
return result;
};
handleReceivedPostsInChannel = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
if (!posts.length) {
handleReceivedPostsInChannel = async (posts?: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
if (!posts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannel method',
);
return [];
}
@@ -120,7 +124,11 @@ const PostsInChannelHandler = (superclass: any) => class extends superclass {
};
handleReceivedPostsInChannelSince = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
if (!posts.length) {
if (!posts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelSince method',
);
return [];
}
@@ -167,7 +175,11 @@ const PostsInChannelHandler = (superclass: any) => class extends superclass {
};
handleReceivedPostsInChannelBefore = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
if (!posts.length) {
if (!posts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelBefore method',
);
return [];
}
@@ -217,7 +229,11 @@ const PostsInChannelHandler = (superclass: any) => class extends superclass {
};
handleReceivedPostForChannel = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
if (!posts.length) {
if (!posts?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "posts" array has been passed to the handleReceivedPostForChannel method',
);
return [];
}

View File

@@ -19,7 +19,11 @@ const {POSTS_IN_THREAD} = Database.MM_TABLES.SERVER;
const PostsInThreadHandler = (superclass: any) => class extends superclass {
handleReceivedPostsInThread = async (postsMap: Record<string, Post[]>, prepareRecordsOnly = false): Promise<PostsInThreadModel[]> => {
if (!Object.keys(postsMap).length) {
if (!postsMap || !Object.keys(postsMap).length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "postsMap" object has been passed to the handleReceivedPostsInThread method',
);
return [];
}

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {transformReactionRecord} from '@database/operator/server_data_operator/transformers/reaction';
import {sanitizeReactions} from '@database/operator/utils/reaction';
@@ -29,10 +28,12 @@ const ReactionHandler = (superclass: any) => class extends superclass {
handleReactions = async ({postsReactions, prepareRecordsOnly, skipSync}: HandleReactionsArgs): Promise<ReactionModel[]> => {
const batchRecords: ReactionModel[] = [];
if (!postsReactions.length) {
throw new DataOperatorException(
'An empty "reactions" array has been passed to the handleReactions method',
if (!postsReactions?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "postsReactions" array has been passed to the handleReactions method',
);
return [];
}
for await (const postReactions of postsReactions) {

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {
buildTeamMembershipKey,
buildTeamSearchHistoryKey,
@@ -51,11 +50,13 @@ const TeamHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<TeamMembershipModel[]>}
*/
handleTeamMemberships = ({teamMemberships, prepareRecordsOnly = true}: HandleTeamMembershipArgs): Promise<TeamMembershipModel[]> => {
if (!teamMemberships.length) {
throw new DataOperatorException(
'An empty "teamMemberships" array has been passed to the handleTeamMemberships method',
handleTeamMemberships = async ({teamMemberships, prepareRecordsOnly = true}: HandleTeamMembershipArgs): Promise<TeamMembershipModel[]> => {
if (!teamMemberships?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "teamMemberships" array has been passed to the handleTeamMemberships method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: teamMemberships, key: 'team_id'});
@@ -78,11 +79,13 @@ const TeamHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<TeamModel[]>}
*/
handleTeam = ({teams, prepareRecordsOnly = true}: HandleTeamArgs): Promise<TeamModel[]> => {
if (!teams.length) {
throw new DataOperatorException(
'An empty "teams" array has been passed to the handleTeam method',
handleTeam = async ({teams, prepareRecordsOnly = true}: HandleTeamArgs): Promise<TeamModel[]> => {
if (!teams?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "teams" array has been passed to the handleTeam method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: teams, key: 'id'});
@@ -104,11 +107,13 @@ const TeamHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<TeamChannelHistoryModel[]>}
*/
handleTeamChannelHistory = ({teamChannelHistories, prepareRecordsOnly = true}: HandleTeamChannelHistoryArgs): Promise<TeamChannelHistoryModel[]> => {
if (!teamChannelHistories.length) {
throw new DataOperatorException(
'An empty "teamChannelHistories" array has been passed to the handleTeamChannelHistory method',
handleTeamChannelHistory = async ({teamChannelHistories, prepareRecordsOnly = true}: HandleTeamChannelHistoryArgs): Promise<TeamChannelHistoryModel[]> => {
if (!teamChannelHistories?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "teamChannelHistories" array has been passed to the handleTeamChannelHistory method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: teamChannelHistories, key: 'id'});
@@ -130,11 +135,13 @@ const TeamHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<TeamSearchHistoryModel[]>}
*/
handleTeamSearchHistory = ({teamSearchHistories, prepareRecordsOnly = true}: HandleTeamSearchHistoryArgs): Promise<TeamSearchHistoryModel[]> => {
if (!teamSearchHistories.length) {
throw new DataOperatorException(
'An empty "teamSearchHistories" array has been passed to the handleTeamSearchHistory method',
handleTeamSearchHistory = async ({teamSearchHistories, prepareRecordsOnly = true}: HandleTeamSearchHistoryArgs): Promise<TeamSearchHistoryModel[]> => {
if (!teamSearchHistories?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "teamSearchHistories" array has been passed to the handleTeamSearchHistory method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: teamSearchHistories, key: 'term'});
@@ -157,11 +164,13 @@ const TeamHandler = (superclass: any) => class extends superclass {
* @throws DataOperatorException
* @returns {Promise<MyTeamModel[]>}
*/
handleMyTeam = ({myTeams, prepareRecordsOnly = true}: HandleMyTeamArgs): Promise<MyTeamModel[]> => {
if (!myTeams.length) {
throw new DataOperatorException(
'An empty "myTeams" array has been passed to the handleSlashCommand method',
handleMyTeam = async ({myTeams, prepareRecordsOnly = true}: HandleMyTeamArgs): Promise<MyTeamModel[]> => {
if (!myTeams?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "myTeams" array has been passed to the handleMyTeam method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: myTeams, key: 'id'});

View File

@@ -4,7 +4,6 @@
import Model from '@nozbe/watermelondb/Model';
import {Database} from '@constants';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {
transformThreadRecord,
transformThreadParticipantRecord,
@@ -36,10 +35,12 @@ const ThreadHandler = (superclass: any) => class extends superclass {
* @returns {Promise<void>}
*/
handleThreads = async ({threads, teamId, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
if (!threads.length) {
throw new DataOperatorException(
'An empty "threads" array has been passed to the handleThreads method',
if (!threads?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "threads" array has been passed to the handleThreads method',
);
return [];
}
// Get unique threads in case they are duplicated
@@ -105,10 +106,12 @@ const ThreadHandler = (superclass: any) => class extends superclass {
handleThreadParticipants = async ({threadsParticipants, prepareRecordsOnly, skipSync = false}: HandleThreadParticipantsArgs): Promise<ThreadParticipantModel[]> => {
const batchRecords: ThreadParticipantModel[] = [];
if (!threadsParticipants.length) {
throw new DataOperatorException(
'An empty "thread participants" array has been passed to the handleThreadParticipants method',
if (!threadsParticipants?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "threadParticipants" array has been passed to the handleThreadParticipants method',
);
return [];
}
for await (const threadParticipant of threadsParticipants) {

View File

@@ -18,7 +18,11 @@ const {THREADS_IN_TEAM} = MM_TABLES.SERVER;
const ThreadInTeamHandler = (superclass: any) => class extends superclass {
handleThreadInTeam = async ({threadsMap, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise<ThreadInTeamModel[]> => {
if (!Object.keys(threadsMap).length) {
if (!threadsMap || !Object.keys(threadsMap).length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "threadsMap" object has been passed to the handleReceivedPostForChannel method',
);
return [];
}

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {MM_TABLES} from '@constants/database';
import DataOperatorException from '@database/exceptions/data_operator_exception';
import {buildPreferenceKey} from '@database/operator/server_data_operator/comparators';
import {
transformPreferenceRecord,
@@ -34,10 +33,12 @@ const UserHandler = (superclass: any) => class extends superclass {
* @returns {Promise<PreferenceModel[]>}
*/
handlePreferences = async ({preferences, prepareRecordsOnly = true, sync = false}: HandlePreferencesArgs): Promise<PreferenceModel[]> => {
if (!preferences.length) {
throw new DataOperatorException(
'An empty "preferences" array has been passed to the handlePreferences method',
if (!preferences?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "preferences" array has been passed to the handlePreferences method',
);
return [];
}
// WE NEED TO SYNC THE PREFS FROM WHAT WE GOT AND WHAT WE HAVE
@@ -86,10 +87,12 @@ const UserHandler = (superclass: any) => class extends superclass {
* @returns {Promise<UserModel[]>}
*/
handleUsers = async ({users, prepareRecordsOnly = true}: HandleUsersArgs): Promise<UserModel[]> => {
if (!users.length) {
throw new DataOperatorException(
'An empty "users" array has been passed to the handleUsers method',
if (!users?.length) {
// eslint-disable-next-line no-console
console.warn(
'An empty or undefined "users" array has been passed to the handleUsers method',
);
return [];
}
const createOrUpdateRawValues = getUniqueRawsBy({raws: users, key: 'id'});