"""GAM People API contacts management.""" import re import json import sys import google.auth import google.auth.exceptions from gamlib import glaction from gamlib import glapi as API from gamlib import glcfg as GC from gamlib import glclargs from gamlib import glentity from gamlib import glgapi as GAPI from gamlib import glglobals as GM from gamlib import glindent from gamlib import glmsgs as Msg from gam.cmd.contacts import ( CONTACTS_ORDERBY_CHOICE_MAP, CONTACTS_PROJECTION_CHOICE_MAP, PEOPLE_ADDRESSES, PEOPLE_ADD_GROUPS_LIST, PEOPLE_BIOGRAPHIES, PEOPLE_BIRTHDAYS, PEOPLE_CALENDAR_URLS, PEOPLE_CLIENT_DATA, PEOPLE_COVER_PHOTOS, PEOPLE_DIRECTORY_MERGE_SOURCES_CHOICE_MAP, PEOPLE_DIRECTORY_SOURCES_CHOICE_MAP, PEOPLE_EMAIL_ADDRESSES, PEOPLE_EVENTS, PEOPLE_EXTERNAL_IDS, PEOPLE_FILE_ASES, PEOPLE_GENDERS, PEOPLE_GROUPS_LIST, PEOPLE_GROUP_NAME, PEOPLE_IM_CLIENTS, PEOPLE_INTERESTS, PEOPLE_LOCALES, PEOPLE_LOCATIONS, PEOPLE_MEMBERSHIPS, PEOPLE_METADATA, PEOPLE_MISC_KEYWORDS, PEOPLE_NAMES, PEOPLE_NICKNAMES, PEOPLE_OCCUPATIONS, PEOPLE_ORGANIZATIONS, PEOPLE_PHONE_NUMBERS, PEOPLE_PHOTOS, PEOPLE_READ_SOURCES_CHOICE_MAP, PEOPLE_RELATIONS, PEOPLE_REMOVE_GROUPS_LIST, PEOPLE_SIP_ADDRESSES, PEOPLE_SKILLS, PEOPLE_UPDATE_TIME, PEOPLE_URLS, PEOPLE_USER_DEFINED, ) Act = glaction.GamAction() Ent = glentity.GamEntity() Ind = glindent.GamIndent() Cmd = glclargs.GamCLArgs() def _getMain(): return sys.modules['gam'] def __getattr__(name): """Fall back to gam module for any undefined names.""" main = _getMain() try: return getattr(main, name) except AttributeError: raise AttributeError(f"module {__name__!r} has no attribute {name!r}") def _initPeopleContactQueryAttributes(printShowCmd): return {'query': None, 'updateTime': None, 'contactGroupSelect': None, 'contactGroupFilter': None, 'group': None, 'dropMemberships': False, 'mainContacts': True, 'otherContacts': printShowCmd, 'emailMatchPattern': None, 'emailMatchType': None} def _getPeopleContactQueryAttributes(contactQuery, myarg, entityType, unknownAction, printShowCmd): if myarg == 'query': contactQuery['query'] = _getMain().getString(Cmd.OB_QUERY) elif myarg in {'contactgroup', 'selectcontactgroup'}: if entityType == Ent.USER: contactQuery['contactGroupSelect'] = _getMain().getString(Cmd.OB_CONTACT_GROUP_ITEM) contactQuery['contactGroupFilter'] = None contactQuery['mainContacts'] = True contactQuery['otherContacts'] = False else: _getMain().unknownArgumentExit() elif myarg == 'emailmatchpattern': contactQuery['emailMatchPattern'] = _getMain().getREPattern(re.IGNORECASE) elif myarg == 'emailmatchtype': contactQuery['emailMatchType'] = _getMain().getString(Cmd.OB_CONTACT_EMAIL_TYPE) elif myarg == 'updatedmin': _getMain().deprecatedArgument(myarg) _getMain().getYYYYMMDD() elif myarg == 'endquery': return False elif not printShowCmd: if unknownAction < 0: _getMain().unknownArgumentExit() if unknownAction > 0: Cmd.Backup() return False elif myarg == 'filtercontactgroup': if entityType == Ent.USER: contactQuery['contactGroupFilter'] = _getMain().getString(Cmd.OB_CONTACT_GROUP_ITEM) contactQuery['contactGroupSelect'] = None contactQuery['mainContacts'] = True contactQuery['otherContacts'] = False else: _getMain().unknownArgumentExit() elif myarg in {'maincontacts', 'selectmaincontacts'}: if entityType == Ent.USER: contactQuery['contactGroupSelect'] = None contactQuery['contactGroupFilter'] = None contactQuery['mainContacts'] = True contactQuery['otherContacts'] = False else: _getMain().unknownArgumentExit() elif myarg in {'othercontacts', 'selectothercontacts'}: if entityType == Ent.USER: contactQuery['contactGroupSelect'] = None contactQuery['contactGroupFilter'] = None contactQuery['mainContacts'] = False contactQuery['otherContacts'] = True else: _getMain().unknownArgumentExit() elif myarg == 'orderby': _getMain().getOrderBySortOrder(CONTACTS_ORDERBY_CHOICE_MAP, 'ascending', False) _getMain().deprecatedArgument(myarg) elif myarg in CONTACTS_PROJECTION_CHOICE_MAP: _getMain().deprecatedArgument(myarg) elif myarg == 'showdeleted': _getMain().deprecatedArgument(myarg) else: if unknownAction < 0: _getMain().unknownArgumentExit() if unknownAction > 0: Cmd.Backup() return False return True PEOPLE_CONTACT_SELECT_ARGUMENTS = { 'query', 'contactgroup', 'selectcontactgroup', 'maincontacts', 'selectmaincontacts', 'othercontacts', 'selectothercontacts', 'emailmatchpattern', 'emailmatchtype', } PEOPLE_CONTACT_DEPRECATED_SELECT_ARGUMENTS = { 'orderby', 'basic', 'thin', 'full', 'showdeleted', } def _getPeopleContactEntityList(entityType, unknownAction, noEntityArguments=None): contactQuery = _initPeopleContactQueryAttributes(False) if noEntityArguments is not None and (not Cmd.ArgumentsRemaining() or Cmd.PeekArgumentPresent(noEntityArguments)): # | are optional in dedup|replacedomain contacts entityList = None queriedContacts = True elif Cmd.PeekArgumentPresent(PEOPLE_CONTACT_SELECT_ARGUMENTS.union(PEOPLE_CONTACT_DEPRECATED_SELECT_ARGUMENTS)): entityList = None queriedContacts = True while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if not _getPeopleContactQueryAttributes(contactQuery, myarg, entityType, unknownAction, False): break else: entityList = _getMain().getEntityList(Cmd.OB_CONTACT_ENTITY) queriedContacts = False if unknownAction < 0: _getMain().checkForExtraneousArguments() return (entityList, entityList if isinstance(entityList, dict) else None, contactQuery, queriedContacts) def _initPeopleOtherContactQueryAttributes(): return {'query': None, 'otherContacts': True, 'emailMatchPattern': None, 'emailMatchType': None} def _getPeopleOtherContactQueryAttributes(contactQuery, myarg, unknownAction): if myarg == 'query': contactQuery['query'] = _getMain().getString(Cmd.OB_QUERY) elif myarg == 'emailmatchpattern': contactQuery['emailMatchPattern'] = _getMain().getREPattern(re.IGNORECASE) elif myarg == 'emailmatchtype': contactQuery['emailMatchType'] = _getMain().getString(Cmd.OB_CONTACT_EMAIL_TYPE) elif myarg == 'endquery': return False else: if unknownAction < 0: _getMain().unknownArgumentExit() if unknownAction > 0: Cmd.Backup() return False return True PEOPLE_OTHERCONTACT_SELECT_ARGUMENTS = {'query', 'emailmatchpattern', 'emailmatchtype'} def _getPeopleOtherContactEntityList(unknownAction): contactQuery = _initPeopleOtherContactQueryAttributes() if Cmd.PeekArgumentPresent(PEOPLE_OTHERCONTACT_SELECT_ARGUMENTS): entityList = None queriedContacts = True while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if not _getPeopleOtherContactQueryAttributes(contactQuery, myarg, unknownAction): break else: entityList = _getMain().getEntityList(Cmd.OB_CONTACT_ENTITY) queriedContacts = False if unknownAction < 0: _getMain().checkForExtraneousArguments() return (entityList, entityList if isinstance(entityList, dict) else None, contactQuery, queriedContacts) def _getPeopleOtherContacts(people, entityType, user, i=0, count=0): try: _getMain().printGettingAllEntityItemsForWhom(Ent.OTHER_CONTACT, user, i, count) results = _getMain().callGAPIpages(people.otherContacts(), 'list', 'otherContacts', pageMessage=_getMain().getPageMessageForWhom(), throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, pageSize=1000, readMask='emailAddresses', fields='nextPageToken,otherContacts(etag,resourceName,emailAddresses(value,type))') otherContacts = {} for contact in results: resourceName = contact.pop('resourceName') otherContacts[resourceName] = contact return otherContacts except GAPI.permissionDenied as e: ClientAPIAccessDeniedExit(str(e)) except (GAPI.serviceNotAvailable, GAPI.forbidden): _getMain().entityUnknownWarning(entityType, user, i, count) return None def queryPeopleContacts(people, contactQuery, fields, sortOrder, entityType, user, i=0, count=0): sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['domaincontact' if entityType == Ent.DOMAIN else 'contact']] _getMain().printGettingAllEntityItemsForWhom(Ent.PEOPLE_CONTACT, user, i, count, query=contactQuery['query']) pageMessage = _getMain().getPageMessageForWhom() try: # Contact group not selected if not contactQuery['contactGroupSelect']: if not contactQuery['query']: results = _getMain().callGAPIpages(people.people().connections(), 'list', 'connections', pageMessage=pageMessage, throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS], resourceName='people/me', sources=sources, personFields=fields, sortOrder=sortOrder, fields='nextPageToken,connections') if contactQuery['contactGroupFilter']: entityList = [] for person in results: for membership in person.get(PEOPLE_MEMBERSHIPS, []): if membership.get('contactGroupMembership', {}).get('contactGroupResourceName', '') == contactQuery['group']: if contactQuery['dropMemberships']: person.pop(PEOPLE_MEMBERSHIPS) entityList.append(person) break else: entityList = results else: results = _getMain().callGAPI(people.people(), 'searchContacts', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, sources=sources, readMask=fields, query=contactQuery['query']) entityList = [person['person'] for person in results.get('results', [])] totalItems = len(entityList) # Contact group selected else: totalItems = _getMain().callGAPI(people.contactGroups(), 'get', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=contactQuery['group'], groupFields='memberCount').get('memberCount', 0) entityList = [] if totalItems > 0: results = _getMain().callGAPI(people.contactGroups(), 'get', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=contactQuery['group'], maxMembers=totalItems, groupFields='name') for resourceName in results.get('memberResourceNames', []): result = _getMain().callGAPI(people.people(), 'get', retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, sources=sources, personFields=fields) entityList.append(result) if pageMessage and (contactQuery['contactGroupSelect'] or contactQuery['contactGroupFilter'] or contactQuery['query']): showMessage = pageMessage.replace(TOTAL_ITEMS_MARKER, str(totalItems)) _getMain().writeGotMessage(showMessage.replace('{0}', str(Ent.Choose(Ent.PEOPLE_CONTACT, totalItems)))) return entityList except (GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) except (GAPI.serviceNotAvailable, GAPI.forbidden): _getMain().entityUnknownWarning(entityType, user, i, count) return None def queryPeopleOtherContacts(people, contactQuery, fields, entityType, user, i=0, count=0): sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['contact']] _getMain().printGettingAllEntityItemsForWhom(Ent.OTHER_CONTACT, user, i, count, query=contactQuery['query']) pageMessage = _getMain().getPageMessageForWhom() try: if not contactQuery['query']: entityList = _getMain().callGAPIpages(people.otherContacts(), 'list', 'otherContacts', pageMessage=pageMessage, throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS], readMask=fields, fields='nextPageToken,otherContacts', sources=sources) else: results = _getMain().callGAPI(people.otherContacts(), 'search', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, pageSize=30, readMask=fields, query=contactQuery['query']) entityList = [person['person'] for person in results.get('results', [])] totalItems = len(entityList) if pageMessage: showMessage = pageMessage.replace(TOTAL_ITEMS_MARKER, str(totalItems)) _getMain().writeGotMessage(showMessage.replace('{0}', str(Ent.Choose(Ent.OTHER_CONTACT, totalItems)))) return entityList except GAPI.permissionDenied as e: ClientAPIAccessDeniedExit(str(e)) except GAPI.forbidden: _getMain().userPeopleServiceNotEnabledWarning(user, i, count) except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, user, i, count) return None def getPeopleContactGroupsInfo(people, entityType, entityName, i, count): contactGroupIDs = {} contactGroupNames = {} try: groups = _getMain().callGAPIpages(people.contactGroups(), 'list', 'contactGroups', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS], groupFields='name', fields='nextPageToken,contactGroups(resourceName,name,formattedName)') if groups: for group in groups: contactGroupIDs[group['resourceName']] = group['formattedName'] contactGroupNames.setdefault(group['formattedName'], []) contactGroupNames[group['formattedName']].append(group['resourceName']) if group['formattedName'] != group['name']: contactGroupNames.setdefault(group['name'], []) contactGroupNames[group['name']].append(group['resourceName']) except GAPI.permissionDenied as e: ClientAPIAccessDeniedExit(str(e)) except GAPI.forbidden: _getMain().userPeopleServiceNotEnabledWarning(entityName, i, count) return (contactGroupIDs, False) except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, entityName, i, count) return (contactGroupIDs, False) return (contactGroupIDs, contactGroupNames) def validatePeopleContactGroup(people, contactGroupName, contactGroupIDs, contactGroupNames, entityType, entityName, i, count): if not contactGroupNames: contactGroupIDs, contactGroupNames = getPeopleContactGroupsInfo(people, entityType, entityName, i, count) if contactGroupNames is False: return (None, contactGroupIDs, contactGroupNames) if contactGroupName == 'clear': return (contactGroupName, contactGroupIDs, contactGroupNames) cg = _getMain().UID_PATTERN.match(contactGroupName) if cg: contactGroupName = cg.group(1) if contactGroupName in contactGroupIDs: return (contactGroupName, contactGroupIDs, contactGroupNames) normalizedContactGroupName = _getMain().normalizeContactGroupResourceName(contactGroupName) if normalizedContactGroupName in contactGroupIDs: return (normalizedContactGroupName, contactGroupIDs, contactGroupNames) else: if contactGroupName in contactGroupIDs: return (contactGroupName, contactGroupIDs, contactGroupNames) if contactGroupName in contactGroupNames: return (contactGroupNames[contactGroupName][0], contactGroupIDs, contactGroupNames) normalizedContactGroupName = _getMain().normalizeContactGroupResourceName(contactGroupName) if normalizedContactGroupName != contactGroupName and normalizedContactGroupName in contactGroupIDs: return (normalizedContactGroupName, contactGroupIDs, contactGroupNames) return (None, contactGroupIDs, contactGroupNames) def validatePeopleContactGroupsList(people, contactId, contactGroupsList, entityType, entityName, i, count): result = True contactGroupIDs = contactGroupNames = None validatedContactGroupsList = [] for contactGroup in contactGroupsList: groupId, contactGroupIDs, contactGroupNames = validatePeopleContactGroup(people, contactGroup, contactGroupIDs, contactGroupNames, entityType, entityName, i, count) if groupId: validatedContactGroupsList.append(groupId) else: if contactGroupNames: _getMain().entityActionNotPerformedWarning([entityType, entityName, Ent.CONTACT, contactId], Ent.TypeNameMessage(Ent.CONTACT_GROUP, contactGroup, Msg.DOES_NOT_EXIST)) result = False return (result, validatedContactGroupsList, contactGroupIDs) # gam create contact + # (contactgroup )* # [(csv [todrive *] (addcsvdata )*))| returnidonly] def createUserPeopleContact(users): entityType = Ent.USER peopleManager = _getMain().PeopleManager() peopleEntityType = Ent.CONTACT sources = PEOPLE_READ_SOURCES_CHOICE_MAP['contact'] parameters = {'csvPF': None, 'titles': ['User', 'resourceName'], 'addCSVData': {}, 'returnIdOnly': False} body, personFields, contactGroupsLists = peopleManager.GetPersonFields(entityType, False, parameters) csvPF = parameters['csvPF'] addCSVData = parameters['addCSVData'] if addCSVData: csvPF.AddTitles(sorted(addCSVData.keys())) returnIdOnly = parameters['returnIdOnly'] i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if contactGroupsLists[PEOPLE_GROUPS_LIST]: result, validatedContactGroupsList, _ = validatePeopleContactGroupsList(people, '', contactGroupsLists[PEOPLE_GROUPS_LIST], entityType, user, i, count) if not result: continue peopleManager.AddContactGroupsToContact(body, validatedContactGroupsList) personFields.add(PEOPLE_MEMBERSHIPS) else: personFields.discard(PEOPLE_MEMBERSHIPS) try: result = _getMain().callGAPI(people.people(), 'createContact', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS+[GAPI.INVALID_ARGUMENT], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, personFields=','.join(personFields), body=body, sources=sources) resourceName = result['resourceName'] if returnIdOnly: _getMain().writeStdout(f'{resourceName}\n') elif not csvPF: _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName], i, count) else: row = {'User': user, 'resourceName': resourceName} if addCSVData: row.update(addCSVData) csvPF.WriteRow(row) except GAPI.invalidArgument as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, None], str(e), i, count) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) if csvPF: csvPF.writeCSVfile('People Contacts') def localPeopleContactSelects(contactQuery, contact): if contactQuery['emailMatchPattern']: emailMatchType = contactQuery['emailMatchType'] for item in contact.get(PEOPLE_EMAIL_ADDRESSES, []): if contactQuery['emailMatchPattern'].match(item['value']): if (not emailMatchType or emailMatchType == item.get('type', '')): break else: return False return True def countLocalPeopleContactSelects(contactQuery, contacts): if contacts is not None and contactQuery['emailMatchPattern']: jcount = 0 for contact in contacts: if localPeopleContactSelects(contactQuery, contact): jcount += 1 else: jcount = len(contacts) if contacts is not None else 0 return jcount def clearPeopleEmailAddressMatches(contactClear, contact): savedAddresses = [] updateRequired = False emailMatchType = contactClear['emailClearType'] for item in contact.get(PEOPLE_EMAIL_ADDRESSES, []): if (contactClear['emailClearPattern'].match(item['value']) and (not emailMatchType or emailMatchType == item.get('type', ''))): updateRequired = True else: savedAddresses.append(item) if updateRequired: contact[PEOPLE_EMAIL_ADDRESSES] = savedAddresses return updateRequired def _clearUpdatePeopleContacts(users, updateContacts): action = Act.Get() entityType = Ent.USER peopleManager = _getMain().PeopleManager() peopleEntityType = Ent.PEOPLE_CONTACT sources = PEOPLE_READ_SOURCES_CHOICE_MAP['contact'] entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleContactEntityList(entityType, 1) if updateContacts: body, updatePersonFields, contactGroupsLists = peopleManager.GetPersonFields(entityType, True) else: contactClear = {'emailClearPattern': contactQuery['emailMatchPattern'], 'emailClearType': contactQuery['emailMatchType']} deleteClearedContactsWithNoEmails = False while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'emailclearpattern': contactClear['emailClearPattern'] = _getMain().getREPattern(re.IGNORECASE) elif myarg == 'emailcleartype': contactClear['emailClearType'] = _getMain().getString(Cmd.OB_CONTACT_EMAIL_TYPE) elif myarg == 'deleteclearedcontactswithnoemails': deleteClearedContactsWithNoEmails = True else: _getMain().unknownArgumentExit() if not contactClear['emailClearPattern']: _getMain().missingArgumentExit('emailclearpattern') i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if contactQuery['contactGroupSelect']: groupId, _, contactGroupNames = validatePeopleContactGroup(people, contactQuery['contactGroupSelect'], None, None, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactQuery['contactGroupSelect']], Msg.DOES_NOT_EXIST, i, count) continue contactQuery['group'] = groupId if queriedContacts: entityList = queryPeopleContacts(people, contactQuery, 'emailAddresses,memberships', None, entityType, user, i, count) if entityList is None: continue Act.Set(action) j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue validatedContactGroupsLists = { PEOPLE_GROUPS_LIST: [], PEOPLE_ADD_GROUPS_LIST: [], PEOPLE_REMOVE_GROUPS_LIST: [] } Ind.Increment() for contact in entityList: j += 1 try: if not queriedContacts: resourceName = contact contact = _getMain().callGAPI(people.people(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=contact, sources=sources, personFields='emailAddresses,memberships') else: if not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] if updateContacts: body['etag'] = contact['etag'] existingContactGroupsList = [] for contactGroup in contact.get(PEOPLE_MEMBERSHIPS, []): if 'contactGroupMembership' in contactGroup: existingContactGroupsList.append(contactGroup['contactGroupMembership']['contactGroupResourceName']) groupError = False for field in [PEOPLE_GROUPS_LIST, PEOPLE_ADD_GROUPS_LIST, PEOPLE_REMOVE_GROUPS_LIST]: if contactGroupsLists[field] and not validatedContactGroupsLists[field]: status, validatedContactGroupsLists[field], _ = validatePeopleContactGroupsList(people, resourceName, contactGroupsLists[field], entityType, user, i, count) if not status: groupError = True if groupError: break if validatedContactGroupsLists[PEOPLE_GROUPS_LIST]: peopleManager.AddContactGroupsToContact(body, validatedContactGroupsLists[PEOPLE_GROUPS_LIST]) updatePersonFields.add(PEOPLE_MEMBERSHIPS) elif validatedContactGroupsLists[PEOPLE_ADD_GROUPS_LIST] or validatedContactGroupsLists[PEOPLE_REMOVE_GROUPS_LIST]: body[PEOPLE_MEMBERSHIPS] = [] if contact.get(PEOPLE_MEMBERSHIPS): peopleManager.AddFilteredContactGroupsToContact(body, existingContactGroupsList, validatedContactGroupsLists[PEOPLE_REMOVE_GROUPS_LIST]) if validatedContactGroupsLists[PEOPLE_ADD_GROUPS_LIST]: peopleManager.AddAdditionalContactGroupsToContact(body, validatedContactGroupsLists[PEOPLE_ADD_GROUPS_LIST]) updatePersonFields.add(PEOPLE_MEMBERSHIPS) elif existingContactGroupsList: updatePersonFields.discard(PEOPLE_MEMBERSHIPS) else: if not clearPeopleEmailAddressMatches(contactClear, contact): continue if deleteClearedContactsWithNoEmails and not contact[PEOPLE_EMAIL_ADDRESSES]: Act.Set(Act.DELETE) _getMain().callGAPI(people.people(), 'deleteContact', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName], j, jcount) continue body = contact updatePersonFields = [PEOPLE_EMAIL_ADDRESSES] person = _getMain().callGAPI(people.people(), 'updateContact', throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, updatePersonFields=','.join(updatePersonFields), body=body, sources=sources) _getMain().entityActionPerformed([entityType, user, peopleEntityType, person['resourceName']], j, jcount) except GAPI.invalidArgument as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) Ind.Decrement() # gam clear contacts | # [emailclearpattern ] [emailcleartype work|home|other|] # [deleteclearedcontactswithnoemails] def clearUserPeopleContacts(users): _clearUpdatePeopleContacts(users, False) # gam update contacts |( endquery) # + # (contactgroup )*|((addcontactgroup )* (removecontactgroup )*) # gam update contacts def updateUserPeopleContacts(users): _clearUpdatePeopleContacts(users, True) def dedupPeopleEmailAddressMatches(emailMatchType, contact): sai = -1 savedAddresses = [] matches = {} updateRequired = False for item in contact.get(PEOPLE_EMAIL_ADDRESSES, []): emailAddr = item['value'] emailType = item.get('type', '') if (emailAddr in matches) and (not emailMatchType or emailType in matches[emailAddr]['types']): if item['metadata'].get('primary', False): savedAddresses[matches[emailAddr]['sai']]['metadata']['primary'] = True updateRequired = True else: savedAddresses.append(item) sai += 1 matches.setdefault(emailAddr, {'types': set(), 'sai': sai}) matches[emailAddr]['types'].add(emailType) if updateRequired: contact[PEOPLE_EMAIL_ADDRESSES] = savedAddresses return updateRequired def replaceDomainPeopleEmailAddressMatches(contactQuery, contact, replaceDomains): updateRequired = False if contactQuery['emailMatchPattern']: emailMatchType = contactQuery['emailMatchType'] for item in contact.get(PEOPLE_EMAIL_ADDRESSES, []): emailAddr = item['value'] emailType = item.get('type', '') if contactQuery['emailMatchPattern'].match(emailAddr): userName, domain = _getMain().splitEmailAddress(emailAddr) domain = domain.lower() if ((domain in replaceDomains) and (not emailMatchType or emailType == emailMatchType)): item['value'] = f'{userName}@{replaceDomains[domain]}' updateRequired = True else: for item in contact.get(PEOPLE_EMAIL_ADDRESSES, []): emailAddr = item['value'] emailType = item.get('type', '') userName, domain = _getMain().splitEmailAddress(emailAddr) domain = domain.lower() if domain in replaceDomains: item['value'] = f'{userName}@{replaceDomains[domain]}' updateRequired = True return updateRequired # gam dedup contacts # [|] # [matchType []] # gam replacedomain contacts # [|] # (domain )+ def dedupReplaceDomainUserPeopleContacts(users): action = Act.Get() entityType = Ent.USER peopleEntityType = Ent.PEOPLE_CONTACT sources = PEOPLE_READ_SOURCES_CHOICE_MAP['contact'] entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleContactEntityList(entityType, 1, {'matchtype', 'domain'}) emailMatchType = False replaceDomains = {} while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if action == Act.DEDUP and myarg == 'matchtype': emailMatchType = _getMain().getBoolean() elif action == Act.REPLACE_DOMAIN and myarg == 'domain': domain = _getMain().getString(Cmd.OB_DOMAIN_NAME).lower() replaceDomains[domain] = _getMain().getString(Cmd.OB_DOMAIN_NAME).lower() else: _getMain().unknownArgumentExit() if action == Act.REPLACE_DOMAIN and not replaceDomains: _getMain().missingArgumentExit('domain') i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if contactQuery['contactGroupSelect']: groupId, _, contactGroupNames = validatePeopleContactGroup(people, contactQuery['contactGroupSelect'], None, None, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactQuery['contactGroupSelect']], Msg.DOES_NOT_EXIST, i, count) continue contactQuery['group'] = groupId if queriedContacts: entityList = queryPeopleContacts(people, contactQuery, 'emailAddresses,memberships', None, entityType, user, i, count) if entityList is None: continue Act.Set(action) j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Act.Set(Act.UPDATE) Ind.Increment() for contact in entityList: j += 1 try: if not queriedContacts: resourceName = contact contact = _getMain().callGAPI(people.people(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=contact, sources=sources, personFields='emailAddresses,memberships') else: if action == Act.DEDUP and not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] if action == Act.DEDUP: if not dedupPeopleEmailAddressMatches(emailMatchType, contact): continue else: if not replaceDomainPeopleEmailAddressMatches(contactQuery, contact, replaceDomains): continue Act.Set(Act.UPDATE) _getMain().callGAPI(people.people(), 'updateContact', throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, updatePersonFields='emailAddresses', body=contact) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName], j, jcount) except GAPI.invalidArgument as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) Ind.Decrement() # gam delete contacts | def deleteUserPeopleContacts(users): entityType = Ent.USER peopleEntityType = Ent.PEOPLE_CONTACT entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleContactEntityList(entityType, -1) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if contactQuery['contactGroupSelect']: groupId, _, contactGroupNames = validatePeopleContactGroup(people, contactQuery['contactGroupSelect'], None, None, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactQuery['contactGroupSelect']], Msg.DOES_NOT_EXIST, i, count) continue contactQuery['group'] = groupId if queriedContacts: entityList = queryPeopleContacts(people, contactQuery, 'emailAddresses', None, entityType, user, i, count) if entityList is None: continue j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contact in entityList: j += 1 if isinstance(contact, dict): if not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] else: resourceName = _getMain().normalizePeopleResourceName(contact) try: _getMain().callGAPI(people.people(), 'deleteContact', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName], j, jcount) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) Ind.Decrement() def _initPersonMetadataParameters(): return {'strip': True, 'mapUpdateTime': False, 'sourceTypes': set()} def _processPersonMetadata(person, parameters): metadata = person.get(PEOPLE_METADATA, None) if metadata is not None: if parameters['mapUpdateTime']: sources = person[PEOPLE_METADATA].get('sources', []) if sources and sources[0].get(PEOPLE_UPDATE_TIME, None) is not None: person[PEOPLE_UPDATE_TIME] = _getMain().formatLocalTime(sources[0][PEOPLE_UPDATE_TIME]) if parameters['sourceTypes']: stripKeys = [] for k, v in person.items(): if isinstance(v, list): person[k] = [] for entry in v: if isinstance(entry, dict): if entry.get('metadata', {}).get('source', {}).get('type', None) in parameters['sourceTypes']: person[k].append(entry) else: person[k].append(entry) if not person[k]: stripKeys.append(k) for k in stripKeys: person.pop(k, None) if parameters['strip']: person.pop(PEOPLE_METADATA, None) for v in person.values(): if isinstance(v, list): for entry in v: if isinstance(entry, dict): entry.pop(PEOPLE_METADATA, None) def addContactGroupNamesToContacts(contacts, contactGroupIDs, showContactGroupNamesList): for contact in contacts: if showContactGroupNamesList: contact[PEOPLE_GROUPS_LIST] = [] for membership in contact.get('memberships', []): if 'contactGroupMembership' in membership: membership['contactGroupMembership']['contactGroupName'] = contactGroupIDs.get(membership['contactGroupMembership']['contactGroupResourceName'], _getMain().UNKNOWN) if showContactGroupNamesList: contact[PEOPLE_GROUPS_LIST].append(membership['contactGroupMembership']['contactGroupName']) def _printPerson(entityTypeName, user, person, csvPF, FJQC, parameters): _processPersonMetadata(person, parameters) row = _getMain().flattenJSON(person, flattened={entityTypeName: user}) if not FJQC.formatJSON: csvPF.WriteRowTitles(row) elif csvPF.CheckRowTitles(row): csvPF.WriteRowNoFilter({entityTypeName: user, 'resourceName': person['resourceName'], 'JSON': json.dumps(_getMain().cleanJSON(person), ensure_ascii=False, sort_keys=True)}) PEOPLE_CONTACT_OBJECT_KEYS = { 'addresses': 'type', 'calendarUrls': 'type', 'emailAddresses': 'type', 'events': 'type', 'externalIds': 'type', 'genders': 'value', 'imClients': 'type', 'locations': 'type', 'miscKeywords': 'type', 'nicknames': 'type', 'organizations': 'type', 'relations': 'type', 'urls': 'type', 'userDefined': 'key', } def _showPerson(userEntityType, user, entityType, person, i, count, FJQC, parameters): _processPersonMetadata(person, parameters) if not FJQC.formatJSON: _getMain().printEntity([userEntityType, user, entityType, person['resourceName']], i, count) Ind.Increment() _getMain().showJSON(None, person, dictObjectsKey=PEOPLE_CONTACT_OBJECT_KEYS) Ind.Decrement() else: _getMain().printLine(json.dumps(_getMain().cleanJSON(person), ensure_ascii=False, sort_keys=True)) def _printPersonEntityList(entityType, entityList, userEntityType, user, i, count, csvPF, FJQC, parameters, contactQuery): if not csvPF: jcount = len(entityList) if not FJQC.formatJSON: _getMain().entityPerformActionModifierNumItems([userEntityType, user], Msg.MAXIMUM_OF, jcount, entityType, i, count) Ind.Increment() j = 0 for person in entityList: j += 1 if not contactQuery or localPeopleContactSelects(contactQuery, person): _showPerson(userEntityType, user, entityType, person, j, jcount, FJQC, parameters) Ind.Decrement() elif entityList: entityTypeName = Ent.Singular(userEntityType) for person in entityList: if not contactQuery or localPeopleContactSelects(contactQuery, person): _printPerson(entityTypeName, user, person, csvPF, FJQC, parameters) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({userEntityType: user}) PEOPLE_FIELDS_CHOICE_MAP = { 'additionalname': PEOPLE_NAMES, 'address': PEOPLE_ADDRESSES, 'addresses': PEOPLE_ADDRESSES, 'ageranges': 'ageRanges', 'billinginfo': PEOPLE_MISC_KEYWORDS, 'biography': PEOPLE_BIOGRAPHIES, 'biographies': PEOPLE_BIOGRAPHIES, 'birthday': PEOPLE_BIRTHDAYS, 'birthdays': PEOPLE_BIRTHDAYS, 'calendar': PEOPLE_CALENDAR_URLS, 'calendars': PEOPLE_CALENDAR_URLS, 'calendarurls': PEOPLE_CALENDAR_URLS, 'clientdata': PEOPLE_CLIENT_DATA, 'coverphotos': PEOPLE_COVER_PHOTOS, 'directoryserver': PEOPLE_MISC_KEYWORDS, 'email': PEOPLE_EMAIL_ADDRESSES, 'emails': PEOPLE_EMAIL_ADDRESSES, 'emailaddresses': PEOPLE_EMAIL_ADDRESSES, 'event': PEOPLE_EVENTS, 'events': PEOPLE_EVENTS, 'externalid': PEOPLE_EXTERNAL_IDS, 'externalids': PEOPLE_EXTERNAL_IDS, 'familyname': PEOPLE_NAMES, 'fileas': PEOPLE_FILE_ASES, 'firstname': PEOPLE_NAMES, 'gender': PEOPLE_GENDERS, 'genders': PEOPLE_GENDERS, 'givenname': PEOPLE_NAMES, 'hobby': PEOPLE_INTERESTS, 'hobbies': PEOPLE_INTERESTS, 'im': PEOPLE_IM_CLIENTS, 'ims': PEOPLE_IM_CLIENTS, 'imclients': PEOPLE_IM_CLIENTS, 'initials': PEOPLE_NICKNAMES, 'interests': PEOPLE_INTERESTS, 'jot': PEOPLE_MISC_KEYWORDS, 'jots': PEOPLE_MISC_KEYWORDS, 'language': PEOPLE_LOCALES, 'languages': PEOPLE_LOCALES, 'lastname': PEOPLE_NAMES, 'locales': PEOPLE_LOCALES, 'location': PEOPLE_LOCATIONS, 'locations': PEOPLE_LOCATIONS, 'maidenname': PEOPLE_NAMES, 'memberships': PEOPLE_MEMBERSHIPS, 'metadata': PEOPLE_METADATA, 'middlename': PEOPLE_NAMES, 'mileage': PEOPLE_MISC_KEYWORDS, 'misckeywords': PEOPLE_MISC_KEYWORDS, 'name': PEOPLE_NAMES, 'names': PEOPLE_NAMES, 'nickname': PEOPLE_NICKNAMES, 'nicknames': PEOPLE_NICKNAMES, 'note': PEOPLE_BIOGRAPHIES, 'notes': PEOPLE_BIOGRAPHIES, 'occupation': PEOPLE_OCCUPATIONS, 'occupations': PEOPLE_OCCUPATIONS, 'organization': PEOPLE_ORGANIZATIONS, 'organizations': PEOPLE_ORGANIZATIONS, 'organisation': PEOPLE_ORGANIZATIONS, 'organisations': PEOPLE_ORGANIZATIONS, 'phone': PEOPLE_PHONE_NUMBERS, 'phones': PEOPLE_PHONE_NUMBERS, 'phonenumbers': PEOPLE_PHONE_NUMBERS, 'photo': PEOPLE_PHOTOS, 'photos': PEOPLE_PHOTOS, 'prefix': PEOPLE_NAMES, 'priority': PEOPLE_MISC_KEYWORDS, 'relation': PEOPLE_RELATIONS, 'relations': PEOPLE_RELATIONS, 'sensitivity': PEOPLE_MISC_KEYWORDS, 'shortname': PEOPLE_NICKNAMES, 'sipaddress': PEOPLE_SIP_ADDRESSES, 'sipaddresses': PEOPLE_SIP_ADDRESSES, 'skills': PEOPLE_SKILLS, 'subject': PEOPLE_MISC_KEYWORDS, 'suffix': PEOPLE_NAMES, 'updated': PEOPLE_UPDATE_TIME, 'updatetime': PEOPLE_UPDATE_TIME, 'urls': PEOPLE_URLS, 'userdefined': PEOPLE_USER_DEFINED, 'userdefinedfield': PEOPLE_USER_DEFINED, 'userdefinedfields': PEOPLE_USER_DEFINED, 'website': PEOPLE_URLS, 'websites': PEOPLE_URLS, } PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP = { 'email': PEOPLE_EMAIL_ADDRESSES, 'emails': PEOPLE_EMAIL_ADDRESSES, 'emailaddresses': PEOPLE_EMAIL_ADDRESSES, 'metadata': PEOPLE_METADATA, 'names': PEOPLE_NAMES, 'phone': PEOPLE_PHONE_NUMBERS, 'phones': PEOPLE_PHONE_NUMBERS, 'phonenumbers': PEOPLE_PHONE_NUMBERS, 'photo': PEOPLE_PHOTOS, 'photos': PEOPLE_PHOTOS, } PEOPLE_CONTACTS_DEFAULT_FIELDS = ['names', 'emailaddresses', 'phonenumbers'] PEOPLE_ORDERBY_CHOICE_MAP = { 'firstname': 'FIRST_NAME_ASCENDING', 'lastname': 'LAST_NAME_ASCENDING', 'lastmodified': 'LAST_MODIFIED_', } def getPersonFieldsList(myarg, fieldsChoiceMap, fieldsList, initialField=None, fieldsArg='fields'): if fieldsList is None: fieldsList = [] return _getMain().getFieldsList(myarg, fieldsChoiceMap, fieldsList, initialField, fieldsArg) def _getPersonFields(fieldsChoiceMap, defaultFields, fieldsList, parameters): if fieldsList is None: fieldsList = [] for field in fieldsChoiceMap: _getMain().addFieldToFieldsList(field, fieldsChoiceMap, fieldsList) elif not fieldsList: for field in defaultFields: _getMain().addFieldToFieldsList(field, fieldsChoiceMap, fieldsList) fieldsList = list(set(fieldsList)) if PEOPLE_UPDATE_TIME in fieldsList: parameters['mapUpdateTime'] = True fieldsList.remove(PEOPLE_UPDATE_TIME) fieldsList.append(PEOPLE_METADATA) return ','.join(fieldsList) def _infoPeople(users, entityType, source): if entityType == Ent.DOMAIN: people = _getMain().buildGAPIObject(API.PEOPLE) peopleEntityType = Ent.DOMAIN_PROFILE if source == 'profile' else Ent.PEOPLE_CONTACT sources = [PEOPLE_READ_SOURCES_CHOICE_MAP[source]] entityList = _getMain().getEntityList(Cmd.OB_CONTACT_ENTITY) resourceNameLists = entityList if isinstance(entityList, dict) else None showContactGroups = False FJQC = _getMain().FormatJSONQuoteChar() fieldsList = [] parameters = _initPersonMetadataParameters() while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'showgroups': showContactGroups = True elif myarg == 'showmetadata': parameters['strip'] = False else: FJQC.GetFormatJSON(myarg) fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] contactGroupIDs = contactGroupNames = None if entityType != Ent.DOMAIN: user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if showContactGroups: contactGroupIDs, contactGroupNames = getPeopleContactGroupsInfo(people, entityType, user, i, count) if contactGroupNames is False: continue j = 0 jcount = len(entityList) if not FJQC.formatJSON: _getMain().entityPerformActionNumItems([entityType, user], jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contact in entityList: j += 1 if isinstance(contact, dict): resourceName = contact['resourceName'] else: resourceName = _getMain().normalizePeopleResourceName(contact) try: result = _getMain().callGAPI(people.people(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR, GAPI.INVALID_ARGUMENT]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, sources=sources, personFields=fields) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) continue except GAPI.invalidArgument as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount) continue except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) if showContactGroups and contactGroupIDs: addContactGroupNamesToContacts([result], contactGroupIDs, False) _showPerson(entityType, user, peopleEntityType, result, j, jcount, FJQC, parameters) Ind.Decrement() # gam info contacts # [showgroups] # [allFields|(fields )] [showmetadata] # [formatjson] def infoUserPeopleContacts(users): _infoPeople(users, Ent.USER, 'contact') # gam print contacts [todrive *] # [showgroups|showgroupnameslist] [orderby firstname|lastname|(lastmodified ascending)|(lastnodified descending) # [allfields|fields ] [showmetadata] # [countsonly|(formatjson [quotechar ])] # gam show contacts # [showgroups] [orderby firstname|lastname|(lastmodified ascending)|(lastnodified descending) # [allfields|(fields )] [showmetadata] # [countsonly|formatjson] def printShowUserPeopleContacts(users): entityType = Ent.USER entityTypeName = Ent.Singular(entityType) csvPF = _getMain().CSVPrintFile([entityTypeName, 'resourceName'], 'sortall') if Act.csvFormat() else None FJQC = _getMain().FormatJSONQuoteChar(csvPF) CSVTitle = 'People Contacts' fieldsList = [] parameters = _initPersonMetadataParameters() sortOrder = None countsOnly = showContactGroups = showContactGroupNamesList = False contactQuery = _initPeopleContactQueryAttributes(True) while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if csvPF and myarg == 'todrive': csvPF.GetTodriveParameters() elif myarg == 'showgroups': showContactGroups = True elif myarg == 'showgroupnameslist': showContactGroups = showContactGroupNamesList = True elif myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'countsonly': countsOnly = True if csvPF: csvPF.SetTitles([entityTypeName, CSVTitle]) elif myarg == 'showmetadata': parameters['strip'] = False elif myarg == 'orderby': sortOrder = _getMain().getChoice(PEOPLE_ORDERBY_CHOICE_MAP, mapChoice=True) if sortOrder == 'LAST_MODIFIED_': sortOrder += _getMain().getChoice(_getMain().SORTORDER_CHOICE_MAP, defaultChoice='DESCENDING', mapChoice=True) elif _getPeopleContactQueryAttributes(contactQuery, myarg, entityType, 0, True): pass else: FJQC.GetFormatJSONQuoteChar(myarg, True) if countsOnly: if csvPF: csvPF.SetFormatJSON(False) fieldsList = ['emailAddresses'] if contactQuery['mainContacts']: fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) if contactQuery['contactGroupFilter'] and 'memberships' not in fields: fields += ',memberships' contactQuery['dropMemberships'] = True if contactQuery['otherContacts']: if not fieldsList: ofields = _getPersonFields(PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) else: ofields = _getMain().getFieldsFromFieldsList([PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP[field.lower()] for field in fieldsList if field.lower() in PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP]) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if contactQuery['otherContacts']: _, opeople = _getMain().buildGAPIServiceObject(API.PEOPLE_OTHERCONTACTS, user, i, count) if not opeople: continue contactGroupIDs = contactGroupNames = None if showContactGroups: contactGroupIDs, contactGroupNames = getPeopleContactGroupsInfo(people, entityType, user, i, count) if contactGroupNames is False: continue contactGroupSelectFilter = contactQuery['contactGroupSelect'] or contactQuery['contactGroupFilter'] if contactGroupSelectFilter: groupId, _, contactGroupNames =\ validatePeopleContactGroup(people, contactGroupSelectFilter, contactGroupIDs, contactGroupNames, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroupSelectFilter], Msg.DOES_NOT_EXIST, i, count) continue contactQuery['group'] = groupId if contactQuery['mainContacts']: contacts = queryPeopleContacts(people, contactQuery, fields, sortOrder, entityType, user, i, count) else: contacts = [] if contactQuery['otherContacts']: ocontacts = queryPeopleOtherContacts(opeople, contactQuery, ofields, entityType, user, i, count) else: ocontacts = [] if countsOnly: jcount = countLocalPeopleContactSelects(contactQuery, contacts)+countLocalPeopleContactSelects(contactQuery, ocontacts) if csvPF: csvPF.WriteRowTitles({entityTypeName: user, CSVTitle: jcount}) else: _getMain().printEntityKVList([entityType, user], [CSVTitle, jcount], i, count) elif contacts is not None or ocontacts is not None: if not csvPF: if contacts is not None and contactQuery['mainContacts']: if showContactGroups and contactGroupIDs: addContactGroupNamesToContacts(contacts, contactGroupIDs, False) _printPersonEntityList(Ent.PEOPLE_CONTACT, contacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) if ocontacts is not None and contactQuery['otherContacts']: _printPersonEntityList(Ent.OTHER_CONTACT, ocontacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) elif contacts or ocontacts: if contacts: if showContactGroups and contactGroupIDs: addContactGroupNamesToContacts(contacts, contactGroupIDs, showContactGroupNamesList and FJQC.formatJSON) _printPersonEntityList(Ent.PEOPLE_CONTACT, contacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) if ocontacts: _printPersonEntityList(Ent.OTHER_CONTACT, ocontacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({Ent.Singular(entityType): user}) if csvPF: csvPF.writeCSVfile(CSVTitle) CONTACTGROUPS_MYCONTACTS_ID = 'contactGroups/myContacts' CONTACTGROUPS_MYCONTACTS_NAME = 'My Contacts' # gam copy othercontacts # | def copyUserPeopleOtherContacts(users): entityType = Ent.USER peopleEntityType = Ent.OTHER_CONTACT sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['contact']] copyMask = ['emailAddresses', 'names', 'phoneNumbers'] entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleOtherContactEntityList(-1) _getMain().checkForExtraneousArguments() i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE_OTHERCONTACTS, user, i, count) if not people: continue if queriedContacts: entityList = queryPeopleOtherContacts(people, contactQuery, 'emailAddresses', entityType, user, i, count) if entityList is None: continue j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contact in entityList: j += 1 if isinstance(contact, dict): if not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] else: resourceName = _getMain().normalizeOtherContactsResourceName(contact) try: _getMain().callGAPI(people.otherContacts(), 'copyOtherContactToMyContactsGroup', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=resourceName, body={'copyMask': ','.join(copyMask), 'sources': sources}) _getMain().entityModifierNewValueActionPerformed([entityType, user, peopleEntityType, resourceName], Act.MODIFIER_TO, CONTACTGROUPS_MYCONTACTS_NAME) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) continue except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) Ind.Decrement() # gam delete othercontacts # | # gam move othercontacts # | # gam update othercontacts # | # * # (contactgroup )* def processUserPeopleOtherContacts(users): action = Act.Get() entityType = Ent.USER peopleEntityType = Ent.OTHER_CONTACT sources = PEOPLE_READ_SOURCES_CHOICE_MAP['contact'] entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleOtherContactEntityList(1) if action == Act.UPDATE: Act.Set(Act.UPDATE_MOVE) peopleManager = _getMain().PeopleManager() body, updatePersonFields, contactGroupsLists = peopleManager.GetPersonFields(entityType, False) else: body = {PEOPLE_MEMBERSHIPS: [{'contactGroupMembership': {'contactGroupResourceName': CONTACTGROUPS_MYCONTACTS_ID}}]} updatePersonFields = [PEOPLE_MEMBERSHIPS] _getMain().checkForExtraneousArguments() validatedContactGroupsList = [CONTACTGROUPS_MYCONTACTS_ID] contactGroupIDs = {CONTACTGROUPS_MYCONTACTS_ID: CONTACTGROUPS_MYCONTACTS_NAME} i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE_OTHERCONTACTS, user, i, count) if not people: continue _, upeople = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not upeople: continue if queriedContacts: entityList = queryPeopleOtherContacts(people, contactQuery, 'emailAddresses', entityType, user, i, count) if entityList is None: continue else: otherContacts = _getPeopleOtherContacts(people, entityType, user, i=0, count=0) if otherContacts is None: continue if action == Act.UPDATE: if contactGroupsLists[PEOPLE_GROUPS_LIST]: result, validatedContactGroupsList, contactGroupIDs =\ validatePeopleContactGroupsList(people, '', contactGroupsLists[PEOPLE_GROUPS_LIST], entityType, user, i, count) if not result: continue if CONTACTGROUPS_MYCONTACTS_ID not in validatedContactGroupsList: validatedContactGroupsList.insert(0, CONTACTGROUPS_MYCONTACTS_ID) updatePersonFields.add(PEOPLE_MEMBERSHIPS) contactGroupNamesList = [] for resourceName in validatedContactGroupsList: contactGroupNamesList.append(contactGroupIDs[resourceName]) contactGroupNames = ','.join(contactGroupNamesList) j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, peopleEntityType, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contact in entityList: j += 1 if isinstance(contact, dict): if not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] else: resourceName = _getMain().normalizeOtherContactsResourceName(contact) contact = otherContacts.get(resourceName) if contact is None: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) continue peopleResourceName = resourceName.replace('otherContacts', 'people') body['etag'] = contact['etag'] if action == Act.UPDATE and validatedContactGroupsList: peopleManager.AddContactGroupsToContact(body, validatedContactGroupsList) try: _getMain().callGAPI(upeople.people(), 'updateContact', throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=peopleResourceName, updatePersonFields=','.join(updatePersonFields), body=body, sources=sources) if action != Act.DELETE: _getMain().entityModifierNewValueActionPerformed([entityType, user, peopleEntityType, resourceName], Act.MODIFIER_TO, contactGroupNames, j, jcount) else: maxRetries = 5 for retry in range(1, maxRetries+1): try: _getMain().callGAPI(upeople.people(), 'deleteContact', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=peopleResourceName) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName], j, jcount) break except (GAPI.notFound, GAPI.internalError): if retry == maxRetries: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) break time.sleep(retry*2) except GAPI.invalidArgument as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount) continue except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) continue except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) Ind.Decrement() # gam print othercontacts [todrive *] # [allfields|(fields )] [showmetadata] # [countsonly|(formatjson [quotechar ])] # gam show othercontacts # [allfields|(fields )] [showmetadata] # [countsonly|formatjson] def printShowUserPeopleOtherContacts(users): entityType = Ent.USER entityTypeName = Ent.Singular(entityType) csvPF = _getMain().CSVPrintFile([entityTypeName, 'resourceName'], 'sortall') if Act.csvFormat() else None FJQC = _getMain().FormatJSONQuoteChar(csvPF) CSVTitle = 'Other Contacts' fieldsList = [] parameters = _initPersonMetadataParameters() countsOnly = False contactQuery = _initPeopleOtherContactQueryAttributes() while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if csvPF and myarg == 'todrive': csvPF.GetTodriveParameters() elif myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'countsonly': countsOnly = True if csvPF: csvPF.SetTitles([entityTypeName, CSVTitle]) elif myarg == 'showmetadata': parameters['strip'] = False elif _getPeopleOtherContactQueryAttributes(contactQuery, myarg, 0): pass else: FJQC.GetFormatJSONQuoteChar(myarg, True) if countsOnly: if csvPF: csvPF.SetFormatJSON(False) fieldsList = ['emailAddresses'] fields = _getPersonFields(PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE_OTHERCONTACTS, user, i, count) if not people: continue contacts = queryPeopleOtherContacts(people, contactQuery, fields, entityType, user, i, count) if countsOnly: jcount = countLocalPeopleContactSelects(contactQuery, contacts) if csvPF: csvPF.WriteRowTitles({entityTypeName: user, CSVTitle: jcount}) else: _getMain().printEntityKVList([entityType, user], [CSVTitle, jcount], i, count) elif contacts is not None: if not csvPF: _printPersonEntityList(Ent.OTHER_CONTACT, contacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) elif contacts: _printPersonEntityList(Ent.OTHER_CONTACT, contacts, entityType, user, i, count, csvPF, FJQC, parameters, contactQuery) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({Ent.Singular(entityType): user}) if csvPF: csvPF.writeCSVfile(CSVTitle) def _printShowPeople(source): people = _getMain().buildGAPIObject(API.PEOPLE_DIRECTORY) entityType = Ent.DOMAIN entityTypeName = Ent.Singular(entityType) sources = [PEOPLE_DIRECTORY_SOURCES_CHOICE_MAP[source]] if sources[0] == 'DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE': peopleEntityType = Ent.DOMAIN_PROFILE CSVTitle = 'People Profiles' else: peopleEntityType = Ent.DOMAIN_PEOPLE_CONTACT CSVTitle = 'People Contacts' function = 'listDirectoryPeople' csvPF = _getMain().CSVPrintFile([entityTypeName, 'resourceName'], 'sortall') if Act.csvFormat() else None FJQC = _getMain().FormatJSONQuoteChar(csvPF) parameters = _initPersonMetadataParameters() mergeSources = [] fieldsList = [] parameters = _initPersonMetadataParameters() countsOnly = False kwargs = {} while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if csvPF and myarg == 'todrive': csvPF.GetTodriveParameters() elif source is None and myarg in {'source', 'sources'}: sources = [_getMain().getChoice(PEOPLE_DIRECTORY_SOURCES_CHOICE_MAP, mapChoice=True)] elif myarg in {'mergesource', 'mergesources'}: mergeSources = [_getMain().getChoice(PEOPLE_DIRECTORY_MERGE_SOURCES_CHOICE_MAP, mapChoice=True)] elif myarg == 'showmetadata': parameters['strip'] = False elif myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'countsonly': countsOnly = True fieldsList = [PEOPLE_METADATA] if csvPF: csvPF.SetTitles([entityTypeName, CSVTitle]) elif myarg == 'query': kwargs['query'] = _getMain().getString(Cmd.OB_QUERY) function = 'searchDirectoryPeople' else: FJQC.GetFormatJSONQuoteChar(myarg, True) if countsOnly and csvPF: csvPF.SetFormatJSON(False) fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) _getMain().printGettingAllEntityItemsForWhom(peopleEntityType, GC.Values[GC.DOMAIN], query=kwargs.get('query')) try: entityList = _getMain().callGAPIpages(people.people(), function, 'people', pageMessage=_getMain().getPageMessageForWhom(), throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS], sources=sources, mergeSources=mergeSources, readMask=fields, fields='nextPageToken,people', **kwargs) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) if not countsOnly: _printPersonEntityList(peopleEntityType, entityList, Ent.DOMAIN, GC.Values[GC.DOMAIN], 0, 0, csvPF, FJQC, parameters, None) else: jcount = len(entityList) if csvPF: csvPF.WriteRowTitles({entityTypeName: GC.Values[GC.DOMAIN], CSVTitle: jcount}) else: _getMain().printEntityKVList([entityType, GC.Values[GC.DOMAIN]], [CSVTitle, jcount]) if csvPF: csvPF.writeCSVfile(CSVTitle) # gam info people|peopleprofile # [allfields|(fields )] [showmetadata] # [formatjson] def doInfoDomainPeopleProfile(): _infoPeople([GC.Values[GC.DOMAIN]], Ent.DOMAIN, 'profile') # gam info peoplecontact # [allfields|(fields )] [showmetadata] # [formatjson] def doInfoDomainPeopleContacts(): _infoPeople([GC.Values[GC.DOMAIN]], Ent.DOMAIN, 'domaincontact') # gam print people|peopleprofile [todrive *] # [query ] # [mergesources ] # [allfields|(fields )] [showmetadata] # [countsonly|(formatjson [quotechar ])] # gam show people|peopleprofile # [query ] # [mergesources ] # [allfields|(fields )] [showmetadata] # [countsonlyformatjson] # gam print domaincontacts|peoplecontacts [todrive *] # [sources ] # [query ] # [mergesources ] # [allfields|(fields )] [showmetadata] # [countsonly|(formatjson [quotechar ])] # gam show domaincontacts|peoplecontacts # [sources ] # [query ] # [mergesources ] # [countsonlyformatjson] def doPrintShowDomainPeopleProfiles(): _printShowPeople('profile') def doPrintShowDomainPeopleContacts(): _printShowPeople('domaincontact') PEOPLE_PROFILE_SOURCETYPE_CHOICE_MAP = { 'account': 'ACCOUNT', 'accounts': 'ACCOUNT', 'domain': 'DOMAIN_PROFILE', 'domains': 'DOMAIN_PROFILE', 'profile': 'PROFILE', 'profiles': 'PROFILE', } # gam print peopleprofile [todrive *] # [allfields|(fields )] [showmetadata] # [sources ] # [formatjson [quotechar ]] # gam show peopleprofile # [allfields|(fields )] [showmetadata] # [sources ] # [formatjson] def printShowUserPeopleProfiles(users): entityType = Ent.USER entityTypeName = Ent.Singular(entityType) sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['profile']] csvPF = _getMain().CSVPrintFile([entityTypeName, 'resourceName'], 'sortall') if Act.csvFormat() else None FJQC = _getMain().FormatJSONQuoteChar(csvPF) fieldsList = [] parameters = _initPersonMetadataParameters() while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if csvPF and myarg == 'todrive': csvPF.GetTodriveParameters() elif myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg in {'source', 'sources'}: for field in _getMain()._getFieldsList(): if field in PEOPLE_PROFILE_SOURCETYPE_CHOICE_MAP: parameters['sourceTypes'].add(PEOPLE_PROFILE_SOURCETYPE_CHOICE_MAP[field]) else: _getMain().invalidChoiceExit(field, PEOPLE_PROFILE_SOURCETYPE_CHOICE_MAP, True) elif myarg == 'showmetadata': parameters['strip'] = False elif myarg == 'peoplelookupuser': _getMain().deprecatedArgument(myarg) else: FJQC.GetFormatJSONQuoteChar(myarg, True) fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 # user, people = buildGAPIServiceObject(API.PEOPLE_DIRECTORY, user, i, count) user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue if csvPF: _getMain().printGettingEntityItemForWhom(Ent.PEOPLE_PROFILE, user, i, count) try: result = _getMain().callGAPI(people.people(), 'get', throwReasons=[GAPI.NOT_FOUND]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName='people/me', sources=sources, personFields=fields) except GAPI.notFound: _getMain().entityUnknownWarning(Ent.PEOPLE_PROFILE, user, i, count) continue except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) if not csvPF: _showPerson(entityType, user, Ent.PEOPLE_PROFILE, result, i, count, FJQC, parameters) else: _printPerson(entityTypeName, user, result, csvPF, FJQC, parameters) if csvPF: csvPF.writeCSVfile('People Profiles') def _processPeopleContactPhotos(users, function): def _makeFilenameFromPattern(resourceName): filename = filenamePattern[:] if subForContactId: filename = filename.replace('#contactid#', resourceName.split('/')[1]) if subForEmail: for email in result.get(PEOPLE_EMAIL_ADDRESSES, []): if email.get(PEOPLE_METADATA, {}).get('primary', False): filename = filename.replace('#email#', email['value']) break else: filename = filename.replace('#email#', resourceName.split('/')[1]) return filename if users is not None: entityType = Ent.USER peopleEntityType = Ent.PEOPLE_CONTACT sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['contact']] else: users = [None] people = _getMain().buildGAPIObject(API.PEOPLE_DIRECTORY) entityType = Ent.DOMAIN peopleEntityType = Ent.PEOPLE_CONTACT sources = [PEOPLE_READ_SOURCES_CHOICE_MAP['domaincontact']] entityList, resourceNameLists, contactQuery, queriedContacts = _getPeopleContactEntityList(entityType, 1) if function in {'updateContactPhoto', 'getContactPhoto'}: sourceFolder = targetFolder = os.getcwd() filenamePattern = '#contactid#.jpg' while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'drivedir': targetFolder = GC.Values[GC.DRIVE_DIR] elif myarg == 'sourcefolder': sourceFolder = _getMain().setFilePath(_getMain().getString(Cmd.OB_FILE_PATH), GC.INPUT_DIR) elif myarg == 'targetfolder': targetFolder = _getMain().setFilePath(_getMain().getString(Cmd.OB_FILE_PATH), GC.DRIVE_DIR) if not os.path.isdir(targetFolder): os.makedirs(targetFolder) elif myarg == 'filename': filenamePattern = _getMain().getString(Cmd.OB_FILE_NAME_PATTERN) else: _getMain().unknownArgumentExit() subForContactId = filenamePattern.find('#contactid#') != -1 subForEmail = filenamePattern.find('#email#') != -1 if not subForContactId and not subForEmail: filename = filenamePattern else: #elif function == 'deleteContactPhoto': _getMain().checkForExtraneousArguments() subForContactId = subForEmail = False i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if resourceNameLists: entityList = resourceNameLists[user] if user is not None: user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue else: user = Ent.Singular(entityType) if contactQuery['contactGroupSelect']: groupId, _, contactGroupNames = validatePeopleContactGroup(people, contactQuery['contactGroupSelect'], None, None, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactQuery['contactGroupSelect']], Msg.DOES_NOT_EXIST, i, count) continue contactQuery['group'] = groupId if queriedContacts: entityList = queryPeopleContacts(people, contactQuery, 'emailAddresses,photos', None, entityType, user, i, count) if entityList is None: continue j = 0 jcount = len(entityList) _getMain().entityPerformActionModifierNumItems([entityType, user], Msg.MAXIMUM_OF, jcount, Ent.PHOTO, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contact in entityList: j += 1 if isinstance(contact, dict): if not localPeopleContactSelects(contactQuery, contact): continue resourceName = contact['resourceName'] else: resourceName = _getMain().normalizePeopleResourceName(contact) try: if subForEmail: result = _getMain().callGAPI(people.people(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, sources=sources, personFields='emailAddresses') if function == 'updateContactPhoto': if subForContactId or subForEmail: filename = _makeFilenameFromPattern(resourceName) filename = os.path.join(sourceFolder, filename) with open(filename, 'rb') as f: image_data = f.read() _getMain().callGAPI(people.people(), function, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=resourceName, body={'photoBytes': base64.urlsafe_b64encode(image_data).decode(_getMain().UTF8)}) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], j, jcount) elif function == 'getContactPhoto': if subForContactId or subForEmail: filename = _makeFilenameFromPattern(resourceName) filename = os.path.join(targetFolder, filename) result = _getMain().callGAPI(people.people(), 'get', throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, resourceName=resourceName, personFields='photos') url = None for photo in result.get('photos', []): if photo['metadata']['source']['type'] == 'CONTACT': url = photo['url'] break if not url: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, None], Msg.CONTACT_PHOTO_NOT_FOUND, j, jcount) continue try: status, photo_data = _getMain().getHttpObj().request(url, 'GET') if status['status'] != '200': _getMain().entityActionFailedWarning([entityType, user, Ent.PHOTO, filename], Msg.NOT_ALLOWED, j, jcount) continue except (httplib2.HttpLib2Error, google.auth.exceptions.TransportError, RuntimeError) as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], str(e), j, jcount) continue status, e = _getMain().writeFileReturnError(filename, photo_data, mode='wb') if status: _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], j, jcount) else: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], str(e), j, jcount) else: #elif function == 'deleteContactPhoto': filename = '' _getMain().callGAPI(people.people(), function, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR, GAPI.PHOTO_NOT_FOUND]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=resourceName) _getMain().entityActionPerformed([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], j, jcount) except (GAPI.notFound, GAPI.internalError): _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount) continue except GAPI.photoNotFound: _getMain().entityDoesNotHaveItemWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], j, jcount) except (GAPI.invalidArgument, OSError, IOError) as e: _getMain().entityActionFailedWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], str(e), j, jcount) except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e: ClientAPIAccessDeniedExit(str(e)) break Ind.Decrement() # gam update contactphotos | # [drivedir|(sourcefolder )] [filename ] def updateUserPeopleContactPhoto(users): _processPeopleContactPhotos(users, 'updateContactPhoto') # gam get contactphotos | # [drivedir|(targetfolder )] [filename ] def getUserPeopleContactPhoto(users): _processPeopleContactPhotos(users, 'getContactPhoto') # gam delete contactphotos | def deleteUserPeopleContactPhoto(users): _processPeopleContactPhotos(users, 'deleteContactPhoto') # gam update contactphotos | # [drivedir|(sourcefolder )] [filename ] def doUpdateDomainContactPhoto(): _processPeopleContactPhotos(None, 'updateContactPhoto') # gam get contactphotos | # [drivedir|(targetfolder )] [filename ] def doGetDomainContactPhoto(): _processPeopleContactPhotos(None, 'getContactPhoto') # gam delete contactphoto | def doDeleteDomainContactPhoto(): _processPeopleContactPhotos(None, 'deleteContactPhoto') # gam create contactgroup + # [(csv [todrive *] (addcsvdata )*))| returnidonly] def createUserPeopleContactGroup(users): peopleManager = _getMain().PeopleManager() entityType = Ent.USER parameters = {'csvPF': None, 'titles': ['User', 'resourceName'], 'addCSVData': {}, 'returnIdOnly': False} body, _ = peopleManager.GetContactGroupFields(parameters) if PEOPLE_GROUP_NAME not in body: return csvPF = parameters['csvPF'] addCSVData = parameters['addCSVData'] if addCSVData: csvPF.AddTitles(sorted(addCSVData.keys())) returnIdOnly = parameters['returnIdOnly'] i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue _, contactGroupNames = getPeopleContactGroupsInfo(people, entityType, user, i, count) if contactGroupNames is False: continue contactGroup = body[PEOPLE_GROUP_NAME] if contactGroup in contactGroupNames: _getMain().entityActionFailedWarning([entityType, user], Ent.TypeNameMessage(Ent.CONTACT_GROUP, contactGroup, Msg.DUPLICATE), i, count) continue try: result = _getMain().callGAPI(people.contactGroups(), 'create', throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, body={'contactGroup': body}, fields='resourceName') resourceName = result['resourceName'] if returnIdOnly: _getMain().writeStdout(f'{resourceName}\n') elif not csvPF: _getMain().entityActionPerformed([entityType, user, Ent.CONTACT_GROUP, resourceName], i, count) else: row = {'User': user, 'resourceName': resourceName} if addCSVData: row.update(addCSVData) csvPF.WriteRow(row) except (GAPI.forbidden, GAPI.permissionDenied): _getMain().userPeopleServiceNotEnabledWarning(user, i, count) except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, user, i, count) if csvPF: csvPF.writeCSVfile('People Contact Groups') # gam update contactgroups + def updateUserPeopleContactGroup(users): peopleManager = _getMain().PeopleManager() entityType = Ent.USER entityList = _getMain().getStringReturnInList(Cmd.OB_CONTACT_GROUP_ITEM) body, fields = peopleManager.GetContactGroupFields() i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue contactGroupIDs = contactGroupNames = None j = 0 jcount = len(entityList) _getMain().entityPerformActionNumItems([entityType, user], jcount, Ent.CONTACT_GROUP, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contactGroup in entityList: j += 1 try: groupId, contactGroupIDs, contactGroupNames = validatePeopleContactGroup(people, contactGroup, contactGroupIDs, contactGroupNames, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], Msg.DOES_NOT_EXIST, j, jcount) continue break result = _getMain().callGAPI(people.contactGroups(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INTERNAL_ERROR]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=groupId) body['etag'] = result['etag'] newContactGroup = body.get(PEOPLE_GROUP_NAME) if newContactGroup: if newContactGroup in contactGroupNames and groupId not in contactGroupNames[newContactGroup]: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], Ent.TypeNameMessage(Ent.CONTACT_GROUP, newContactGroup, Msg.DUPLICATE), i, count) continue _getMain().callGAPI(people.contactGroups(), 'update', throwReasons=[GAPI.NOT_FOUND]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=groupId, body={'contactGroup': body, 'updateGroupFields': fields}) _getMain().entityActionPerformed([entityType, user, Ent.CONTACT_GROUP, contactGroup], j, jcount) except (GAPI.notFound, GAPI.internalError) as e: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], str(e), j, jcount) except (GAPI.forbidden, GAPI.permissionDenied): _getMain().userPeopleServiceNotEnabledWarning(user, i, count) break except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, user, i, count) break Ind.Decrement() # gam delete contactgroups def deleteUserPeopleContactGroups(users): entityType = Ent.USER entityList = _getMain().getEntityList(Cmd.OB_CONTACT_GROUP_ENTITY, shlexSplit=True) contactGroupIdLists = entityList if isinstance(entityList, dict) else None i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if contactGroupIdLists: entityList = contactGroupIdLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue contactGroupIDs = contactGroupNames = None j = 0 jcount = len(entityList) _getMain().entityPerformActionNumItems([entityType, user], jcount, Ent.CONTACT_GROUP, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contactGroup in entityList: j += 1 try: groupId, contactGroupIDs, contactGroupNames = validatePeopleContactGroup(people, contactGroup, contactGroupIDs, contactGroupNames, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], Msg.DOES_NOT_EXIST, j, jcount) continue break _getMain().callGAPI(people.contactGroups(), 'delete', throwReasons=[GAPI.NOT_FOUND]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=groupId) _getMain().entityActionPerformed([entityType, user, Ent.CONTACT_GROUP, contactGroup], j, jcount) except GAPI.notFound as e: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], str(e), j, jcount) except (GAPI.forbidden, GAPI.permissionDenied): _getMain().userPeopleServiceNotEnabledWarning(user, i, count) break except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, user, i, count) break Ind.Decrement() PEOPLE_GROUP_TIME_OBJECTS = {'updateTime'} def _normalizeContactGroupMetadata(contactGroup): normalizedContactGroup = contactGroup.copy() for k, v in normalizedContactGroup.pop('metadata', {}).items(): normalizedContactGroup[k] = v return normalizedContactGroup def _showContactGroup(userEntityType, user, entityType, contactGroup, i, count, FJQC): if FJQC.formatJSON: _getMain().printLine(json.dumps(_getMain().cleanJSON(contactGroup, timeObjects=PEOPLE_GROUP_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)) return normalizedContactGroup = _normalizeContactGroupMetadata(contactGroup) _getMain().printEntity([userEntityType, user, entityType, contactGroup['resourceName']], i, count) Ind.Increment() _getMain().showJSON(None, normalizedContactGroup, timeObjects=PEOPLE_GROUP_TIME_OBJECTS) Ind.Decrement() def _printContactGroup(entityTypeName, user, contactGroup, csvPF, FJQC): normalizedContactGroup = _normalizeContactGroupMetadata(contactGroup) row = _getMain().flattenJSON(normalizedContactGroup, flattened={entityTypeName: user}, timeObjects=PEOPLE_GROUP_TIME_OBJECTS) if not FJQC.formatJSON: csvPF.WriteRowTitles(row) elif csvPF.CheckRowTitles(row): csvPF.WriteRowNoFilter({entityTypeName: user, 'resourceName': contactGroup['resourceName'], 'JSON': json.dumps(_getMain().cleanJSON(contactGroup, timeObjects=PEOPLE_GROUP_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)}) PEOPLE_CONTACTGROUPS_FIELDS_CHOICE_MAP = { 'clientdata': 'clientData', 'grouptype': 'groupType', 'membercount': 'memberCount', 'metadata': 'metadata', 'name': 'name', } PEOPLE_CONTACTGROUPS_DEFAULT_FIELDS = ['name', 'metadata', 'grouptype', 'membercount'] # gam info contactgroups # [allfields|(fields )] [showmetadata] # [formatjson] def infoUserPeopleContactGroups(users): entityType = Ent.USER entityList = _getMain().getEntityList(Cmd.OB_CONTACT_GROUP_ENTITY, shlexSplit=True) contactGroupIdLists = entityList if isinstance(entityList, dict) else None FJQC = _getMain().FormatJSONQuoteChar() fieldsList = [] parameters = _initPersonMetadataParameters() while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_CONTACTGROUPS_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'showmetadata': parameters['strip'] = False else: FJQC.GetFormatJSON(myarg) fields = _getPersonFields(PEOPLE_CONTACTGROUPS_FIELDS_CHOICE_MAP, PEOPLE_CONTACTGROUPS_DEFAULT_FIELDS, fieldsList, parameters) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 if contactGroupIdLists: entityList = contactGroupIdLists[user] user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue contactGroupIDs = contactGroupNames = None j = 0 jcount = len(entityList) if not FJQC.formatJSON: _getMain().entityPerformActionNumItems([entityType, user], jcount, Ent.CONTACT_GROUP, i, count) if jcount == 0: _getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC) continue Ind.Increment() for contactGroup in entityList: j += 1 try: groupId, contactGroupIDs, contactGroupNames = validatePeopleContactGroup(people, contactGroup, contactGroupIDs, contactGroupNames, entityType, user, i, count) if not groupId: if contactGroupNames: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], Msg.DOES_NOT_EXIST, j, jcount) continue break group = _getMain().callGAPI(people.contactGroups(), 'get', throwReasons=[GAPI.NOT_FOUND]+GAPI.PEOPLE_ACCESS_THROW_REASONS, resourceName=groupId, groupFields=fields) _showContactGroup(entityType, user, Ent.CONTACT_GROUP, group, j, jcount, FJQC) except GAPI.notFound as e: _getMain().entityActionFailedWarning([entityType, user, Ent.CONTACT_GROUP, contactGroup], str(e), j, jcount) except (GAPI.forbidden, GAPI.permissionDenied): _getMain().userPeopleServiceNotEnabledWarning(user, i, count) break except GAPI.serviceNotAvailable: _getMain().entityUnknownWarning(entityType, user, i, count) break Ind.Decrement() # gam print contactgroups [todrive *] # [allfields|(fields )] [showmetadata] # [formatjson [quotechar ]] # gam show contactgroups # [allfields|(fields )] [showmetadata] # [formatjson] def printShowUserPeopleContactGroups(users): entityType = Ent.USER entityTypeName = Ent.Singular(entityType) csvPF = _getMain().CSVPrintFile([entityTypeName, 'resourceName'], 'sortall') if Act.csvFormat() else None if csvPF: csvPF.SetNoEscapeChar(True) FJQC = _getMain().FormatJSONQuoteChar(csvPF) fieldsList = [] parameters = _initPersonMetadataParameters() while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if csvPF and myarg == 'todrive': csvPF.GetTodriveParameters() elif myarg == 'allfields': fieldsList = None elif getPersonFieldsList(myarg, PEOPLE_CONTACTGROUPS_FIELDS_CHOICE_MAP, fieldsList): pass elif myarg == 'showmetadata': parameters['strip'] = False else: FJQC.GetFormatJSONQuoteChar(myarg, True) fields = _getPersonFields(PEOPLE_CONTACTGROUPS_FIELDS_CHOICE_MAP, PEOPLE_CONTACTGROUPS_DEFAULT_FIELDS, fieldsList, parameters) i, count, users = _getMain().getEntityArgument(users) for user in users: i += 1 user, people = _getMain().buildGAPIServiceObject(API.PEOPLE, user, i, count) if not people: continue _getMain().printGettingAllEntityItemsForWhom(Ent.PEOPLE_CONTACT_GROUP, user, i, count) try: entityList = _getMain().callGAPIpages(people.contactGroups(), 'list', 'contactGroups', pageMessage=_getMain().getPageMessageForWhom(), throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS, pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS], groupFields=fields, fields='nextPageToken,contactGroups') except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied) as e: ClientAPIAccessDeniedExit(str(e)) _printPersonEntityList(Ent.PEOPLE_CONTACT_GROUP, entityList, entityType, user, i, count, csvPF, FJQC, parameters, None) if csvPF: csvPF.writeCSVfile('People Contact Groups') # Delegate command utilities