diff --git a/docs/Aliases.md b/docs/Aliases.md index 2f6844d7..7aa0fa6d 100644 --- a/docs/Aliases.md +++ b/docs/Aliases.md @@ -55,13 +55,18 @@ gam create alias bob[@yourdomain.com] user robert[@yourdomain.com] The existing alias is deleted and a new alias is created. ``` gam update alias|aliases user|group|target | - [notargetverify] + [notargetverify] [waitafterdelete ] ``` `` are the aliases, `` is the target. By default, GAM makes additional API calls to verify that the target email address exists before updating the alias; if you know that the target exists, you can suppress the verification with `notargetverify. +GAM updates an alias to point to a new target by deleting the alias and then recreates the alias pointing to the new target. +Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error. +Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists, +use the option `waitafterdelete ` to increase the wait time to a maximum of 10 seconds. + ## Delete an alias regardless of the target ``` gam delete alias|aliases [user|group|target] diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index bcaa9628..8290f8ce 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -10,6 +10,25 @@ Add the `-s` option to the end of the above commands to suppress creating the `g See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation +### 6.66.14 + +Updated `gam modify messages` to recognize the following error: +``` +ERROR: 400: invalid - Invalid label: SENT +``` + +Updated `gam update alias user|group|target ` +to avoid the following problem. +``` +$ gam update alias testalias@domain.com user testuser +User Alias: testalias@domain.com, Deleted +User Alias: testalias@domain.com, User: testuser@domain.com, Update Failed: Duplicate, Email Address: testalias@domain.com +``` +GAM updates an alias to point to a new target by deleting the alias and then recreating the alias pointing to the new target. +Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error. +Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists, +the option `waitafterdelete ` can be used to increase the wait time to a maximum of 10 seconds. + ### 6.66.13 Updated functionality of option `preservefiletimes` in `gam update drivefile `. diff --git a/docs/How-to-Upgrade-from-Standard-GAM.md b/docs/How-to-Upgrade-from-Standard-GAM.md index b2524157..dfce0b4d 100644 --- a/docs/How-to-Upgrade-from-Standard-GAM.md +++ b/docs/How-to-Upgrade-from-Standard-GAM.md @@ -334,7 +334,7 @@ writes the credentials into the file oauth2.txt. admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found -GAMADV-XTD3 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.10.8 64-bit final MacOS High Sierra 10.13.6 x86_64 @@ -1002,7 +1002,7 @@ writes the credentials into the file oauth2.txt. C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt C:\GAMADV-XTD3>gam version WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found -GAMADV-XTD3 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.0 64-bit final Windows-10-10.0.17134 AMD64 diff --git a/docs/Version-and-Help.md b/docs/Version-and-Help.md index 0c043b78..d8e27696 100644 --- a/docs/Version-and-Help.md +++ b/docs/Version-and-Help.md @@ -4,7 +4,7 @@ Print the current version of Gam with details ``` gam version -GAMADV-XTD3 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.0 64-bit final MacOS Monterey 12.7 x86_64 @@ -16,7 +16,7 @@ Time: 2023-06-02T21:10:00-07:00 Print the current version of Gam with details and time offset information ``` gam version timeoffset -GAMADV-XTD3 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.0 64-bit final MacOS Monterey 12.7 x86_64 @@ -28,7 +28,7 @@ Your system time differs from www.googleapis.com by less than 1 second Print the current version of Gam with extended details and SSL information ``` gam version extended -GAMADV-XTD3 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.0 64-bit final MacOS Monterey 12.7 x86_64 @@ -65,7 +65,7 @@ MacOS High Sierra 10.13.6 x86_64 Path: /Users/Admin/bin/gamadv-xtd3 Version Check: Current: 5.35.08 - Latest: 6.66.13 + Latest: 6.66.14 echo $? 1 ``` @@ -73,7 +73,7 @@ echo $? Print the current version number without details ``` gam version simple -6.66.13 +6.66.14 ``` In Linux/MacOS you can do: ``` @@ -83,7 +83,7 @@ echo $VER Print the current version of Gam and address of this Wiki ``` gam help -GAM 6.66.13 - https://github.com/taers232c/GAMADV-XTD3 +GAM 6.66.14 - https://github.com/taers232c/GAMADV-XTD3 Ross Scroggs Python 3.12.0 64-bit final MacOS Monterey 12.7 x86_64 diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 41392731..fbab9420 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -1457,7 +1457,7 @@ gam print alertfeedback [todrive *] [alert ] [filter gam create|add alias|aliases user|group|target | [verifynotinvitable] gam update alias|aliases user|group|target | - [notargetverify] + [notargetverify] [waitafterdelete ] gam delete alias|aliases [user|group|target] gam remove aliases|nicknames user|group gam delete alias|aliases diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index 9cb2a871..9a0019a2 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -2,6 +2,25 @@ Merged GAM-Team version +6.66.14 + +Updated `gam modify messages` to recognize the following error: +``` +ERROR: 400: invalid - Invalid label: SENT +``` + +Updated `gam update alias user|group|target ` +to avoid the following problem. +``` +$ gam update alias testalias@domain.com user testuser +User Alias: testalias@domain.com, Deleted +User Alias: testalias@domain.com, User: testuser@domain.com, Update Failed: Duplicate, Email Address: testalias@domain.com +``` +GAM updates an alias to point to a new target by deleting the alias and then recreating the alias pointing to the new target. +Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error. +Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists, +the option `waitafterdelete ` can be used to increase the wait time to a maximum of 10 seconds. + 6.66.13 Updated functionality of option `preservefiletimes` in `gam update drivefile `. diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 0d9bcf97..1c2ac9d8 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -370,7 +370,7 @@ YUBIKEY_VALUE_ERROR_RC = 85 YUBIKEY_MULTIPLE_CONNECTED_RC = 86 YUBIKEY_NOT_FOUND_RC = 87 -# Multiprocessing lock +# Multiprocessing lock mplock = None # stdin/stdout/stderr @@ -17333,7 +17333,7 @@ ALIAS_TARGET_TYPES = ['user', 'group', 'target'] # gam create aliases|nicknames user|group|target | # [verifynotinvitable] # gam update aliases|nicknames user|group|target | -# [notargetverify] +# [notargetverify] [waitafterdelete ] def doCreateUpdateAliases(): def verifyAliasTargetExists(): if targetType != 'group': @@ -17355,6 +17355,42 @@ def doCreateUpdateAliases(): GAPI.badRequest, GAPI.invalid, GAPI.systemError): return None + def deleteAliasOnUpdate(): +# User alias + if targetType != 'group': + try: + callGAPI(cd.users().aliases(), 'delete', + throwReasons=[GAPI.USER_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.FORBIDDEN, GAPI.INVALID_RESOURCE, + GAPI.CONDITION_NOT_MET], + userKey=aliasEmail, alias=aliasEmail) + printEntityKVList([Ent.USER_ALIAS, aliasEmail], [Act.PerformedName(Act.DELETE)], i, count) + time.sleep(waitAfterDelete) + return True + except GAPI.conditionNotMet as e: + entityActionFailedWarning([Ent.USER_ALIAS, aliasEmail], str(e), i, count) + return False + except (GAPI.userNotFound, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.invalidResource): + if targetType == 'user': + entityUnknownWarning(Ent.USER_ALIAS, aliasEmail, i, count) + return False +# Group alias + try: + callGAPI(cd.groups().aliases(), 'delete', + throwReasons=[GAPI.GROUP_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.FORBIDDEN, GAPI.INVALID_RESOURCE, + GAPI.CONDITION_NOT_MET], + groupKey=aliasEmail, alias=aliasEmail) + time.sleep(waitAfterDelete) + return True + except GAPI.conditionNotMet as e: + entityActionFailedWarning([Ent.GROUP_ALIAS, aliasEmail], str(e), i, count) + return False + except GAPI.forbidden: + entityUnknownWarning(Ent.GROUP_ALIAS, aliasEmail, i, count) + return False + except (GAPI.groupNotFound, GAPI.badRequest, GAPI.invalid, GAPI.invalidResource): + entityUnknownWarning(Ent.GROUP_ALIAS, aliasEmail, i, count) + return False + cd = buildGAPIObject(API.DIRECTORY) ci = None updateCmd = Act.Get() == Act.UPDATE @@ -17364,12 +17400,15 @@ def doCreateUpdateAliases(): entityLists = targetEmails if isinstance(targetEmails, dict) else None verifyNotInvitable = False verifyTarget = updateCmd + waitAfterDelete = 2 while Cmd.ArgumentsRemaining(): myarg = getArgument() if (not updateCmd) and myarg == 'verifynotinvitable': verifyNotInvitable = True elif updateCmd and myarg == 'notargetverify': verifyTarget = False + elif updateCmd and myarg == 'waitafterdelete': + waitAfterDelete = getInteger(minVal=2, maxVal=10) else: unknownArgumentExit() i = 0 @@ -17394,30 +17433,9 @@ def doCreateUpdateAliases(): if targetType is None: entityUnknownWarning(Ent.ALIAS_TARGET, targetEmail, i, count) continue - if updateCmd: - try: - callGAPI(cd.users().aliases(), 'delete', - throwReasons=[GAPI.USER_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.FORBIDDEN, GAPI.INVALID_RESOURCE, - GAPI.CONDITION_NOT_MET], - userKey=aliasEmail, alias=aliasEmail) - printEntityKVList([Ent.USER_ALIAS, aliasEmail], [Act.PerformedName(Act.DELETE)], i, count) - except GAPI.conditionNotMet as e: - entityActionFailedWarning([Ent.USER_ALIAS, aliasEmail], str(e), i, count) - continue - except (GAPI.userNotFound, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.invalidResource): - try: - callGAPI(cd.groups().aliases(), 'delete', - throwReasons=[GAPI.GROUP_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.FORBIDDEN, GAPI.INVALID_RESOURCE, - GAPI.CONDITION_NOT_MET], - groupKey=aliasEmail, alias=aliasEmail) - except GAPI.conditionNotMet as e: - entityActionFailedWarning([Ent.GROUP_ALIAS, aliasEmail], str(e), i, count) - continue - except GAPI.forbidden: - entityUnknownWarning(Ent.GROUP_ALIAS, aliasEmail, i, count) - continue - except (GAPI.groupNotFound, GAPI.badRequest, GAPI.invalid, GAPI.invalidResource): - entityUnknownWarning(Ent.ALIAS, aliasEmail, i, count) + if updateCmd and not deleteAliasOnUpdate(): + continue +# User alias if targetType != 'group': try: callGAPI(cd.users().aliases(), 'insert', @@ -17440,6 +17458,7 @@ def doCreateUpdateAliases(): if targetType == 'user': entityUnknownWarning(Ent.ALIAS_TARGET, targetEmail, i, count) continue +# Group alias try: callGAPI(cd.groups().aliases(), 'insert', throwReasons=[GAPI.GROUP_NOT_FOUND, GAPI.USER_NOT_FOUND, GAPI.BAD_REQUEST, @@ -25169,16 +25188,18 @@ def createChatMember(users): entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return + kvList.extend([entityType, '']) Ind.Increment() j = 0 for body in members: + j += 1 kvList[-1] = body[field]['name'] try: member = callGAPI(chat.spaces().members(), 'create', bailOnInternalError=True, - throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], + throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], parent=parent, body=body) - if role != 'ROLE_MEMBER': + if role != 'ROLE_MEMBER' and entityType == Ent.CHAT_MANAGER_USER: member = callGAPI(chat.spaces().members(), 'patch', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], @@ -25193,7 +25214,7 @@ def createChatMember(users): Ind.Decrement() else: writeStdout(f'{member["name"]}\n') - except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: + except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: entityActionFailedWarning(kvList, str(e)) Ind.Decrement() @@ -25230,6 +25251,7 @@ def createChatMember(users): missingArgumentExit('space') if not userList and not groupList: missingArgumentExit('user|members|group|groups') + userEntityType = Ent.CHAT_MEMBER_USER if role == 'ROLE_MEMBER' else Ent.CHAT_MANAGER_USER userMembers = [] for user in userList: name = normalizeEmailAddressOrUID(user) @@ -25244,10 +25266,12 @@ def createChatMember(users): user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count, [Ent.CHAT_SPACE, parent]) if not chat: continue + Ind.Increment() if userMembers: - addMembers(userMembers, 'member', Ent.CHAT_MEMBER_USER, i, count) + addMembers(userMembers, 'member', userEntityType, i, count) if groupMembers: addMembers(groupMembers, 'groupMember', Ent.CHAT_MEMBER_GROUP, i, count) + Ind.Decrement() def _deleteChatMembers(chat, kvList, jcount, memberNames, i, count): j = 0 @@ -25323,7 +25347,7 @@ def deleteUpdateChatMember(users): i, count, users = getEntityArgument(users) for user in users: i += 1 - user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count) + user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count, [Ent.CHAT_SPACE, parent] if parent is not None else None) if not chat: continue jcount = len(memberNames) @@ -25360,12 +25384,12 @@ CHAT_SYNC_PREVIEW_TITLES = ['space', 'member', 'role', 'action', 'message'] # [preview [actioncsv]] # (users )* (groups )* def syncChatMembers(users): - def _previewAction(members, jcount, action): + def _previewAction(members, entityType, jcount, action): Ind.Increment() j = 0 for member in members: j += 1 - entityActionPerformed([Ent.CHAT_SPACE, parent, Ent.CHAT_MEMBER, member, Ent.ROLE, role], j, jcount) + entityActionPerformed([Ent.CHAT_SPACE, parent, entityType, member, Ent.ROLE, role], j, jcount) Ind.Decrement() if csvPF: for member in members: @@ -25373,11 +25397,11 @@ def syncChatMembers(users): def addMembers(memberNames, members, entityType, i, count): jcount = len(memberNames) - entityPerformActionNumItems(kvList, jcount, Ent.CHAT_MEMBER, i, count) + entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return if preview: - _previewAction(memberNames, jcount, Act.REMOVE) + _previewAction(memberNames, entityType, jcount, Act.REMOVE) return kvList.extend([entityType, '']) Ind.Increment() @@ -25389,26 +25413,26 @@ def syncChatMembers(users): try: callGAPI(chat.spaces().members(), 'create', bailOnInternalError=True, - throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], + throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], parent=parent, body=body) - if role != 'ROLE_MEMBER': + if role != 'ROLE_MEMBER' and entityType == Ent.CHAT_MANAGER_USER: callGAPI(chat.spaces().members(), 'patch', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], name=memberName, updateMask='role', body={'role': role}) entityActionPerformed(kvList, j, jcount) - except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: + except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: entityActionFailedWarning(kvList, str(e), j, jcount) Ind.Decrement() del kvList[-2:] def deleteMembers(memberNames, entityType, i, count): jcount = len(memberNames) - entityPerformActionNumItems(kvList, jcount, Ent.CHAT_MEMBER, i, count) + entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return if preview: - _previewAction(memberNames, jcount, Act.ADD) + _previewAction(memberNames, entityType, jcount, Act.ADD) return kvList.extend([entityType, '']) Ind.Increment() @@ -25417,7 +25441,6 @@ def syncChatMembers(users): del kvList[-2:] cd = buildGAPIObject(API.DIRECTORY) - kwargs = {} parent = None role = CHAT_MEMBER_ROLE_MAP['member'] mtype = CHAT_MEMBER_TYPE_MAP['human'] @@ -25456,6 +25479,7 @@ def syncChatMembers(users): unknownArgumentExit() if not parent: missingArgumentExit('space') + userEntityType = Ent.CHAT_MEMBER_USER if role == 'ROLE_MEMBER' else Ent.CHAT_MANAGER_USER userMembers = {} syncUsersSet = set() for user in userList: @@ -25470,12 +25494,11 @@ def syncChatMembers(users): memberName = f'{parent}/members/{name}' groupMembers[memberName] = {'groupMember': {'name': f'groups/{name}'}} syncGroupsSet.add(memberName) - kwargs['filter'] = f'member.type = "{mtype}" AND role = "{role}"' - qfilter = f'{Ent.Singular(Ent.CHAT_SPACE)}: {parent}, {kwargs["filter"]}' + qfilter = f'{Ent.Singular(Ent.CHAT_SPACE)}: {parent}' i, count, users = getEntityArgument(users) for user in users: i += 1 - user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count) + user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count, [Ent.CHAT_SPACE, parent]) if not chat: continue currentUsersSet = set() @@ -25484,12 +25507,14 @@ def syncChatMembers(users): members = callGAPIpages(chat.spaces().members(), 'list', 'memberships', pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, i, count, qfilter), throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], - pageSize=CHAT_PAGE_SIZE, parent=parent, **kwargs) + pageSize=CHAT_PAGE_SIZE, parent=parent, showGroups=groupsSpecified) for member in members: - _getChatMemberEmail(cd, member) if 'member' in member: - currentUsersSet.add(f"{parent}/members/{member['member']['email']}") + if member['member']['type'] == mtype and member['role'] == role: + _getChatMemberEmail(cd, member) + currentUsersSet.add(f"{parent}/members/{member['member']['email']}") elif 'groupMember' in member: + _getChatMemberEmail(cd, member) currentGroupsSet.add(f"{parent}/members/{member['groupMember']['email']}") except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e: exitIfChatNotConfigured(chat, kvList, str(e), i, count) @@ -25497,13 +25522,13 @@ def syncChatMembers(users): if syncOperation != 'addonly': Act.Set([Act.REMOVE, Act.REMOVE_PREVIEW][preview]) if usersSpecified: - deleteMembers(currentUsersSet-syncUsersSet, Ent.CHAT_MEMBER_USER, i, count) + deleteMembers(currentUsersSet-syncUsersSet, userEntityType, i, count) if groupsSpecified: deleteMembers(currentGroupsSet-syncGroupsSet, Ent.CHAT_MEMBER_GROUP, i, count) if syncOperation != 'removeonly': Act.Set([Act.ADD, Act.ADD_PREVIEW][preview]) if usersSpecified: - addMembers(syncUsersSet-currentUsersSet, userMembers, Ent.CHAT_MEMBER_USER, i, count) + addMembers(syncUsersSet-currentUsersSet, userMembers, userEntityType, i, count) if groupsSpecified: addMembers(syncGroupsSet-currentGroupsSet, groupMembers, Ent.CHAT_MEMBER_GROUP, i, count) if csvPF: @@ -65484,20 +65509,27 @@ def _processMessagesThreads(users, entityType): bcount = min(jcount-mcount, GC.Values[GC.MESSAGE_BATCH_SIZE]) while bcount > 0: body['ids'] = messageIds[mcount:mcount+bcount] + idsCount = min(5, bcount) + idsList = ','.join(body['ids'][0:idsCount]) + if bcount > 5: + idsList += ',...' try: callGAPI(gmail.users().messages(), function, - throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.INVALID_MESSAGE_ID, GAPI.FAILED_PRECONDITION], + throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.INVALID_MESSAGE_ID, GAPI.INVALID, GAPI.FAILED_PRECONDITION], userId='me', body=body) for messageId in body['ids']: mcount += 1 entityActionPerformed([Ent.USER, user, entityType, messageId], mcount, jcount) except (GAPI.serviceNotAvailable, GAPI.badRequest): mcount += bcount + except GAPI.invalid as e: + entityActionFailedWarning([Ent.USER, user, entityType, idsList], f'{str(e)} ({mcount+1}-{mcount+bcount}/{jcount})') + mcount += bcount except GAPI.invalidMessageId: - entityActionFailedWarning([Ent.USER, user, entityType, Msg.BATCH], f'{Msg.INVALID_MESSAGE_ID} ({mcount+1}-{mcount+bcount}/{jcount})') + entityActionFailedWarning([Ent.USER, user, entityType, idsList], f'{Msg.INVALID_MESSAGE_ID} ({mcount+1}-{mcount+bcount}/{jcount})') mcount += bcount except GAPI.failedPrecondition: - entityActionFailedWarning([Ent.USER, user, entityType, Msg.BATCH], f'{Msg.FAILED_PRECONDITION} ({mcount+1}-{mcount+bcount}/{jcount})') + entityActionFailedWarning([Ent.USER, user, entityType, idsList], f'{Msg.FAILED_PRECONDITION} ({mcount+1}-{mcount+bcount}/{jcount})') mcount += bcount bcount = min(jcount-mcount, GC.Values[GC.MESSAGE_BATCH_SIZE]) diff --git a/src/gam/gamlib/glentity.py b/src/gam/gamlib/glentity.py index 0a3a2b93..b0d6d917 100644 --- a/src/gam/gamlib/glentity.py +++ b/src/gam/gamlib/glentity.py @@ -84,6 +84,7 @@ class GamEntity(): CHANNEL_PRODUCT = 'chpr' CHANNEL_SKU = 'chsk' CHAT_BOT = 'chbo' + CHAT_MANAGER_USER = 'chgu' CHAT_MEMBER = 'chme' CHAT_MEMBER_GROUP = 'chmg' CHAT_MEMBER_USER = 'chmu' @@ -416,6 +417,7 @@ class GamEntity(): CHANNEL_PRODUCT: ['Channel Products', 'Channel Product'], CHANNEL_SKU: ['Channel SKUs', 'Channel SKU'], CHAT_BOT: ['Chat BOTs', 'Chat BOT'], + CHAT_MANAGER_USER: ['Chat User Managers', 'Chat User Manager'], CHAT_MESSAGE: ['Chat Messages', 'Chat Message'], CHAT_MESSAGE_ID: ['Chat Message IDs', 'Chat Message ID'], CHAT_MEMBER: ['Chat Members', 'Chat Member'],