Added variable csv_output_header_required to gam.cfg
Some checks failed
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Intel Windows, windows-2025) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 14, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 18, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled

This commit is contained in:
Ross Scroggs
2026-02-11 16:19:03 -08:00
parent 0290f0d0ce
commit 4f79a29155
4 changed files with 49 additions and 9 deletions

View File

@@ -1,3 +1,20 @@
7.34.00
Added variable `csv_output_header_required` to `gam.cfg` that is a comma separated list of `<Strings>`
that are required to be in the list of column headers in the CSV file written by a gam print command.
This will typically be used to specify headers that are required in subsequent commands that process
the CSV file even if the API didn't return any data for those columns.
Updated the following commands to not require the `Directory API - Domains` scope
unless the `internal` or `external` options are used to request the member category.
```
gam info|print groups
gam print|show group-members
gam info|print cigroups
gam print|show cigroup-members
gam <UserTypeEntity> print|show filesharecounts
```
7.33.03 7.33.03
Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>` Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>`

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.33.03' __version__ = '7.34.00'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
# pylint: disable=wrong-import-position # pylint: disable=wrong-import-position
@@ -4177,6 +4177,7 @@ def SetGlobalVariables():
GC.Values[GC.CSV_OUTPUT_HEADER_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_FILTER) GC.Values[GC.CSV_OUTPUT_HEADER_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_FILTER)
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_DROP_FILTER) GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_DROP_FILTER)
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_ORDER) GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_ORDER)
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_REQUIRED)
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER) GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER)
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = _getCfgChoice(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER_MODE) GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = _getCfgChoice(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER_MODE)
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_DROP_FILTER) GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_DROP_FILTER)
@@ -4311,6 +4312,8 @@ def SetGlobalVariables():
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE] = GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE][:] GC.Values[GC.CSV_OUTPUT_HEADER_FORCE] = GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE][:]
if not GC.Values[GC.CSV_OUTPUT_HEADER_ORDER]: if not GC.Values[GC.CSV_OUTPUT_HEADER_ORDER]:
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER][:] GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER][:]
if not GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED]:
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED][:]
if not GC.Values[GC.CSV_OUTPUT_ROW_FILTER]: if not GC.Values[GC.CSV_OUTPUT_ROW_FILTER]:
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER][:] GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER][:]
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE] GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE]
@@ -6084,7 +6087,7 @@ def _checkMemberCategory(member, memberDisplayOptions):
if memberDisplayOptions['showCategory']: if memberDisplayOptions['showCategory']:
member['category'] = category member['category'] = category
if memberDisplayOptions['checkCategory']: if memberDisplayOptions['checkCategory']:
return True if memberDisplayOptions[category] else False return bool(memberDisplayOptions[category])
return True return True
def _checkCIMemberCategory(member, memberDisplayOptions): def _checkCIMemberCategory(member, memberDisplayOptions):
@@ -6097,7 +6100,7 @@ def _checkCIMemberCategory(member, memberDisplayOptions):
if memberDisplayOptions['showCategory']: if memberDisplayOptions['showCategory']:
member['category'] = category member['category'] = category
if memberDisplayOptions['checkCategory']: if memberDisplayOptions['checkCategory']:
return True if memberDisplayOptions[category] else False return bool(memberDisplayOptions[category])
return True return True
def getCIGroupMemberRoleFixType(member): def getCIGroupMemberRoleFixType(member):
@@ -7922,6 +7925,7 @@ class CSVPrintFile():
self.JSONtitlesList = [] self.JSONtitlesList = []
self.sortHeaders = [] self.sortHeaders = []
self.SetHeaderForce(GC.Values[GC.CSV_OUTPUT_HEADER_FORCE]) self.SetHeaderForce(GC.Values[GC.CSV_OUTPUT_HEADER_FORCE])
self.SetHeaderRequired(GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED])
if not self.headerForce and titles is not None: if not self.headerForce and titles is not None:
self.SetTitles(titles) self.SetTitles(titles)
self.SetJSONTitles(titles) self.SetJSONTitles(titles)
@@ -8638,6 +8642,9 @@ class CSVPrintFile():
self.SetTitles(headerForce) self.SetTitles(headerForce)
self.SetJSONTitles(headerForce) self.SetJSONTitles(headerForce)
def SetHeaderRequired(self, headerRequired):
self.headerRequired = headerRequired
def SetHeaderOrder(self, headerOrder): def SetHeaderOrder(self, headerOrder):
self.headerOrder = headerOrder self.headerOrder = headerOrder
@@ -9081,6 +9088,8 @@ class CSVPrintFile():
extrasaction = 'raise' extrasaction = 'raise'
if not self.formatJSON: if not self.formatJSON:
if not self.headerForce: if not self.headerForce:
if self.headerRequired:
self.AddTitles(self.headerRequired)
self.SortTitles() self.SortTitles()
self.SortIndexedTitles(self.titlesList) self.SortIndexedTitles(self.titlesList)
if self.fixPaths: if self.fixPaths:
@@ -9098,6 +9107,17 @@ class CSVPrintFile():
titlesList = self.titlesList titlesList = self.titlesList
else: else:
if not self.headerForce: if not self.headerForce:
if self.headerRequired:
for i, v in enumerate(self.JSONtitlesList):
if v.startswith('JSON'):
j = i
for title in self.headerRequired:
self.JSONtitlesList.insert(j, title)
self.JSONtitlesSet.add(title)
j += 1
break
else:
self.AddJSONTitles(self.headerRequired)
if self.fixPaths: if self.fixPaths:
self.FixPathsTitles(self.JSONtitlesList) self.FixPathsTitles(self.JSONtitlesList)
if not self.rows and self.nodataFields is not None: if not self.rows and self.nodataFields is not None:
@@ -9907,7 +9927,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
csvColumnDelimiter, csvNoEscapeChar, csvQuoteChar, csvColumnDelimiter, csvNoEscapeChar, csvQuoteChar,
csvSortHeaders, csvTimestampColumn, csvSortHeaders, csvTimestampColumn,
csvHeaderFilter, csvHeaderDropFilter, csvHeaderFilter, csvHeaderDropFilter,
csvHeaderForce, csvHeaderOrder, csvHeaderForce, csvHeaderOrder, csvHeaderRequired,
csvRowFilter, csvRowFilterMode, csvRowDropFilter, csvRowDropFilterMode, csvRowFilter, csvRowFilterMode, csvRowDropFilter, csvRowDropFilterMode,
csvRowLimit, csvRowLimit,
showGettings, showGettingsGotNL, showGettings, showGettingsGotNL,
@@ -9932,6 +9952,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
GM.Globals[GM.CSV_OUTPUT_HEADER_FILTER] = csvHeaderFilter[:] GM.Globals[GM.CSV_OUTPUT_HEADER_FILTER] = csvHeaderFilter[:]
GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE] = csvHeaderForce[:] GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE] = csvHeaderForce[:]
GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER] = csvHeaderOrder[:] GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER] = csvHeaderOrder[:]
GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED] = csvHeaderRequired[:]
GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR] = csvQuoteChar GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR] = csvQuoteChar
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER] = csvRowDropFilter[:] GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER] = csvRowDropFilter[:]
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = csvRowDropFilterMode GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = csvRowDropFilterMode
@@ -10156,6 +10177,7 @@ def MultiprocessGAMCommands(items, showCmds):
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER], GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER],
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE], GC.Values[GC.CSV_OUTPUT_HEADER_FORCE],
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER], GC.Values[GC.CSV_OUTPUT_HEADER_ORDER],
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED],
GC.Values[GC.CSV_OUTPUT_ROW_FILTER], GC.Values[GC.CSV_OUTPUT_ROW_FILTER],
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE], GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE],
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER], GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER],
@@ -34846,13 +34868,14 @@ def finalizeInternalDomains(cd, internalDomains):
return internalDomains return internalDomains
def finalizeIPSGMGroupRolesMemberDisplayOptions(cd, memberDisplayOptions, verifyAllowExternal): def finalizeIPSGMGroupRolesMemberDisplayOptions(cd, memberDisplayOptions, verifyAllowExternal):
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
if verifyAllowExternal: if verifyAllowExternal:
memberDisplayOptions['external'] = memberDisplayOptions['checkCategory'] = memberDisplayOptions['showCategory'] = True memberDisplayOptions['external'] = memberDisplayOptions['checkCategory'] = memberDisplayOptions['showCategory'] = True
memberDisplayOptions['internal'] = False memberDisplayOptions['internal'] = False
if memberDisplayOptions['showCategory']: if memberDisplayOptions['showCategory']:
memberDisplayOptions['gs'] = buildGAPIObject(API.GROUPSSETTINGS) memberDisplayOptions['gs'] = buildGAPIObject(API.GROUPSSETTINGS)
memberDisplayOptions['checkShowCategory'] = memberDisplayOptions['checkCategory'] or memberDisplayOptions['showCategory'] memberDisplayOptions['checkShowCategory'] = memberDisplayOptions['checkCategory'] or memberDisplayOptions['showCategory']
if memberDisplayOptions['checkShowCategory']:
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
return memberDisplayOptions['showCategory'], memberDisplayOptions['checkShowCategory'] return memberDisplayOptions['showCategory'], memberDisplayOptions['checkShowCategory']
GROUP_FIELDS_CHOICE_MAP = { GROUP_FIELDS_CHOICE_MAP = {

View File

@@ -331,7 +331,7 @@ CSV_INPUT_ROW_FILTER_ITEMS = {CSV_INPUT_ROW_FILTER, CSV_INPUT_ROW_FILTER_MODE,
CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER, CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER,
CSV_OUTPUT_HEADER_FORCE, CSV_OUTPUT_HEADER_ORDER, CSV_OUTPUT_HEADER_FORCE, CSV_OUTPUT_HEADER_ORDER,
# CSV_OUTPUT_HEADER_REQUIRED, CSV_OUTPUT_HEADER_REQUIRED,
CSV_OUTPUT_ROW_FILTER, CSV_OUTPUT_ROW_FILTER_MODE, CSV_OUTPUT_ROW_FILTER, CSV_OUTPUT_ROW_FILTER_MODE,
CSV_OUTPUT_ROW_DROP_FILTER, CSV_OUTPUT_ROW_DROP_FILTER_MODE, CSV_OUTPUT_ROW_DROP_FILTER, CSV_OUTPUT_ROW_DROP_FILTER_MODE,
CSV_OUTPUT_ROW_LIMIT} CSV_OUTPUT_ROW_LIMIT}
@@ -376,7 +376,7 @@ Defaults = {
CSV_OUTPUT_HEADER_DROP_FILTER: '', CSV_OUTPUT_HEADER_DROP_FILTER: '',
CSV_OUTPUT_HEADER_FORCE: '', CSV_OUTPUT_HEADER_FORCE: '',
CSV_OUTPUT_HEADER_ORDER: '', CSV_OUTPUT_HEADER_ORDER: '',
# CSV_OUTPUT_HEADER_REQUIRED: '', CSV_OUTPUT_HEADER_REQUIRED: '',
CSV_OUTPUT_LINE_TERMINATOR: 'lf', CSV_OUTPUT_LINE_TERMINATOR: 'lf',
CSV_OUTPUT_QUOTE_CHAR: '\'"\'', CSV_OUTPUT_QUOTE_CHAR: '\'"\'',
CSV_OUTPUT_ROW_FILTER: '', CSV_OUTPUT_ROW_FILTER: '',
@@ -550,7 +550,7 @@ VAR_INFO = {
CSV_OUTPUT_HEADER_DROP_FILTER: {VAR_TYPE: TYPE_HEADERFILTER}, CSV_OUTPUT_HEADER_DROP_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED}, CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
CSV_OUTPUT_HEADER_ORDER: {VAR_TYPE: TYPE_HEADERORDER}, CSV_OUTPUT_HEADER_ORDER: {VAR_TYPE: TYPE_HEADERORDER},
# CSV_OUTPUT_HEADER_REQUIRED: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED}, CSV_OUTPUT_HEADER_REQUIRED: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
CSV_OUTPUT_LINE_TERMINATOR: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'cr': '\r', 'lf': '\n', 'crlf': '\r\n'}}, CSV_OUTPUT_LINE_TERMINATOR: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'cr': '\r', 'lf': '\n', 'crlf': '\r\n'}},
CSV_OUTPUT_QUOTE_CHAR: {VAR_TYPE: TYPE_CHARACTER}, CSV_OUTPUT_QUOTE_CHAR: {VAR_TYPE: TYPE_CHARACTER},
CSV_OUTPUT_ROW_FILTER: {VAR_TYPE: TYPE_ROWFILTER}, CSV_OUTPUT_ROW_FILTER: {VAR_TYPE: TYPE_ROWFILTER},

View File

@@ -250,7 +250,7 @@ Globals = {
CSV_OUTPUT_HEADER_FILTER: [], CSV_OUTPUT_HEADER_FILTER: [],
CSV_OUTPUT_HEADER_FORCE: [], CSV_OUTPUT_HEADER_FORCE: [],
CSV_OUTPUT_HEADER_ORDER: [], CSV_OUTPUT_HEADER_ORDER: [],
# CSV_OUTPUT_HEADER_REQUIRED: [], CSV_OUTPUT_HEADER_REQUIRED: [],
CSV_OUTPUT_NO_ESCAPE_CHAR: None, CSV_OUTPUT_NO_ESCAPE_CHAR: None,
CSV_OUTPUT_QUOTE_CHAR: None, CSV_OUTPUT_QUOTE_CHAR: None,
CSV_OUTPUT_ROW_DROP_FILTER: [], CSV_OUTPUT_ROW_DROP_FILTER: [],