Compare commits

..

6 Commits

Author SHA1 Message Date
Ross Scroggs
19304f95e8 Added command to check if an OU contains items 2024-07-21 13:14:15 -07:00
Ross Scroggs
5b49b8c957 Added option showitemcountonly to gam print domainaliasess 2024-07-19 16:11:57 -07:00
Ross Scroggs
f1e599d535 Added option showitemcountonly to gam print domains 2024-07-18 13:19:41 -07:00
Ross Scroggs
752b502399 Fixed bug in gam <UserTypeEntity> print filelist that caused a trap. 2024-07-18 07:07:54 -07:00
Ross Scroggs
8e3d562830 Revert to setuptools 70.3.0 2024-07-17 19:05:54 -07:00
Ross Scroggs
5b6c7a30d7 Updated gam calendars <CalendarEntity> import event icaluid <iCalUID> json <JSONdata> 2024-07-17 16:50:04 -07:00
15 changed files with 442 additions and 193 deletions

View File

@@ -5,8 +5,10 @@
- [Promote a domain to be primary](#promote-a-domain-to-be-primary)
- [Delete a domain](#delete-a-domain)
- [Display domains](#display-domains)
- [Display domains count](#display-domains-count)
- [Create and delete domain aliases](#create-and-delete-domain-aliases)
- [Display domain aliases](#display-domain-aliases)
- [Display domain aliases count](#display-domain-aliases-count)
## API documentation
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/domains
@@ -30,15 +32,18 @@ gam delete domain <DomainName>
```
## Display domains
```
gam info domain [<DomainName>] [formatjson]
gam show domains [formatjson]
gam info domain [<DomainName>]
[formatjson]
gam show domains
[formatjson]
```
For `info`, if `<DomainName>` is omitted, information about the primary domain will be displayed.
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
```
gam print domains [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
gam print domains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
```
By default, Gam displays the information as columns of fields.
* `formatjson` - Display the fields in JSON format.
@@ -49,6 +54,13 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display domains count
Display the number of domains.
```
gam print|show domains
showitemcountonly
```
## Create and delete domain aliases
```
gam create domainalias|aliasdomain <DomainAlias> <DomainName>
@@ -56,13 +68,16 @@ gam delete domainalias|aliasdomain <DomainAlias>
```
## Display domain aliases
```
gam info domainalias|aliasdomain <DomainAlias> [formatjson]
gam show domainaliases|aliasdomains [formatjson] [formatjson [quotechar <Character>]]
gam info domainalias|aliasdomain <DomainAlias>
[formatjson]
gam show domainaliases|aliasdomains
[formatjson]
```
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
```
gam print domainaliases|aliasdomains [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
gam print domainaliases|aliasdomains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
```
By default, Gam displays the information as columns of fields.
* `formatjson` - Display the fields in JSON format.
@@ -73,3 +88,9 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display domain aliases count
Display the number of domain aliases.
```
gam print|show domainaliases|aliasdomains
showitemcountonly
```

View File

@@ -23,42 +23,42 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
- `gamadv-xtd3-6.wx.yz-linux-x86_64-glibc2.19.tar.xz`
- `gamadv-xtd3-6.wx.yz-linux-x86_64-legacy.tar.xz`
- Download the archive, extract the contents into some directory.
- Start a terminal session and cd to the install directory.
- Start a terminal session.
* Executable Archive, Manual, Raspberry Pi/ChromeOS ARM devices
- `gamadv-xtd3-6.wx.yz-linux-arm64-glibc2.31.tar.xz`
- `gamadv-xtd3-6.wx.yz-linux-arm64-glibc2.27.tar.xz`
- `gamadv-xtd3-6.wx.yz-linux-arm64-glibc2.23.tar.xz`
- Download the archive, extract the contents into some directory.
- Start a terminal session and cd to the install directory.
- Start a terminal session.
* Executable Archive, Manual, Mac OS versions Big Sur, Monterey, Ventura - M1/M2
- `gamadv-xtd3-6.wx.yz-macos-arm64.tar.xz`
- Download the archive, extract the contents into some directory.
- Start a terminal session and cd to the install directory.
- Start a terminal session.
* Executable Archive, Manual, Mac OS, versions Big Sur, Monterey, Ventura - Intel
- `gamadv-xtd3-6.wx.yz-macos-x86_64.tar.xz`
- Download the archive, extract the contents into some directory.
- Start a terminal session and cd to the install directory.
- Start a terminal session.
* Executable Archive, Manual, Windows 64 bit
- `gamadv-xtd3-6.wx.yz-windows-x86_64.zip`
- Download the archive, extract the contents into some directory.
- Start a Command Prompt/PowerShell session and cd to the install directory.
- Start a Command Prompt/PowerShell session.
* Executable Installer, Manual, Windows 64 bit
- `gamadv-xtd3-6.wx.yz-windows-x86_64.msi`
- Download the installer and run it.
- Start a Command Prompt/PowerShell session and cd to the install directory.
- Start a Command Prompt/PowerShell session.
* Winget
- `winget install taers232c.GAMADV-XTD3 --location C:\GAMADV-XTD3`
- Specify an alternate location if desired
- Start a Command Prompt/PowerShell session and cd to the install directory.
- Start a Command Prompt/PowerShell session.
* Source, all platforms
- `Source code(zip)`
- `Source code(tar.gz)`
- Download the archive, extract the contents into some directory.
- Start a terminal/Command Prompt/PowerShell session and cd to the install directory.
- Start a terminal/Command Prompt/PowerShell session.

View File

@@ -32,6 +32,7 @@ ENTITY_IS_A_USER_ALIAS_RC = 21
ENTITY_IS_A_GROUP_RC = 22
ENTITY_IS_A_GROUP_ALIAS_RC = 23
ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC = 24
ORGUNIT_NOT_EMPTY_RC = 25
CHECK_USER_GROUPS_ERROR_RC = 29
ORPHANS_COLLECTED_RC = 30
# Warnings/Errors
@@ -61,4 +62,14 @@ TARGET_DRIVE_SPACE_ERROR_RC = 74
USER_REQUIRED_TO_CHANGE_PASSWORD_ERROR_RC = 75
USER_SUSPENDED_ERROR_RC = 76
NO_CSV_DATA_TO_UPLOAD_RC = 77
NO_SA_ACCESS_CONTEXT_MANAGER_EDITOR_ROLE_RC = 78
ACCESS_POLICY_ERROR_RC = 79
YUBIKEY_CONNECTION_ERROR_RC = 80
YUBIKEY_INVALID_KEY_TYPE_RC = 81
YUBIKEY_INVALID_SLOT_RC = 82
YUBIKEY_INVALID_PIN_RC = 83
YUBIKEY_APDU_ERROR_RC = 84
YUBIKEY_VALUE_ERROR_RC = 85
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
YUBIKEY_NOT_FOUND_RC = 87
```

View File

@@ -8,7 +8,33 @@ Automatic update to the latest version on Linux/Mac OS/Google Cloud Shell/Raspbe
By default, a folder, `gamadv-xtd3`, is created in the default or specified path and the files are downloaded into that folder.
Add the `-s` option to the end of the above commands to suppress creating the `gamadv-xtd3` folder; the files are downloaded directly into the default or specified path.
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation
See [Downloads-Installs](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads-Installs) for Windows or other options, including manual installation
### 6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU
as it must not contain any items in order to be deleted.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#check-organizational-unit-for-contained-items
### 6.77.18
Added option `showitemcountonly` to `gam print domainaliases` that causes GAM to display the
number of domain aliasess on stdout; no CSV file is written.
### 6.77.17
Added option `showitemcountonly` to `gam print domains` that causes GAM to display the
number of domains on stdout; no CSV file is written.
### 6.77.16
Fixed bug in `gam <UserTypeEntity> print filelist` that caused a trap.
### 6.77.15
Updated `gam calendars <CalendarEntity> import event icaluid <iCalUID> json <JSONdata>` to handle API
constraints on recurring events.
### 6.77.14

View File

@@ -39,7 +39,7 @@ To run all commands properly, GAMADV-XTD3 requires three things:
Use these steps if you have never used any version of GAM in your domain. They will create a GAM project
and all necessary authentications.
* Download: [Downloads](Downloads)
* Download: [Downloads-Installs](Downloads-Installs)
* Configuration: [GAM Configuration](gam.cfg)
* Install: [How to Install Advanced GAM](How-to-Install-Advanced-GAM)
@@ -47,7 +47,7 @@ and all necessary authentications.
Use these steps if you have used any version of GAM in your domain. They will update your GAM project
and all necessary authentications.
* Download: [Downloads](Downloads)
* Download: [Downloads-Installs](Downloads-Installs)
* Configuration: [GAM Configuration](gam.cfg)
* Upgrade: [How to Upgrade from Standard GAM](How-to-Upgrade-from-Standard-GAM)
@@ -56,7 +56,7 @@ Use these steps if you already use GAMADV-X or GAMADV-XTD or GAMADV-XTD3. The up
or authentications because new features have been included.
* Updates: [GAM Updates]
* Download: [Downloads](Downloads)
* Download: [Downloads-Installs](Downloads-Installs)
You can install multiple versions of GAM and GAMADV-XTD3 in different parallel directories.

View File

@@ -27,7 +27,7 @@ substitute that value in the directions.
Make the directory:
```
mkdir -p /Users/admin/GAMconfig
mkdir -p /Users/admin/GAMConfig
```
Add the following line:
@@ -42,14 +42,7 @@ to one of these files based on your shell:
~/.profile
```
You need to enable this setting in the environment. The easiest way is probably to close your terminal and open a new session. This will load the environment variables, including the one you just added. Test this by issuing this command:
```
echo $GAMCFGDIR
```
This should print the name of the directory you used above.
Alternatively, without starting a new session, load the new variable in this session directly: issue the following command replacing `<Filename>` with the name of the file you edited:
Issue the following command replacing `<Filename>` with the name of the file you edited:
```
source <Filename>
```
@@ -66,7 +59,11 @@ data in this folder and execute GAM commands from this folder. You should not us
/Users/admin/bin/gamadv-xtd3 or /Users/admin/GAMConfig for this purpose.
This example assumes that the GAM working directory will be /Users/admin/GAMWork; If you've chosen
another directory, substitute that value in the directions.
* Make the /Users/admin/GAMWork directory before proceeding.
Make the directory:
```
mkdir -p /Users/admin/GAMWork
```
### Set an alias
You should set an alias to point to /Users/admin/bin/gamadv-xtd3/gam so you can operate from the /Users/admin/GAMWork directory.
@@ -103,9 +100,12 @@ Created: /Users/admin/GAMConfig
Created: /Users/admin/GAMConfig/gamcache
Config File: /Users/admin/GAMConfig/gam.cfg, Initialized
Section: DEFAULT
activity_max_results = 100
...
[long list of all config settings that should match the directories you specified]
cache_dir = /Users/admin/GAMConfig/gamcache
...
config_dir = /Users/admin/GAMConfig
...
drive_dir = /Users/admin/GAMWork
...
admin@server:/Users/admin$
@@ -476,9 +476,12 @@ Default Language: en
admin@server:/Users/admin$ gam config customer_id C01234567 domain domain.com timezone local save verify
Config File: /Users/admin/GAMConfig/gam.cfg, Saved
Section: DEFAULT
activity_max_results = 100
...
[long list of all config settings that should match the data you specified]
customer_id = C01234567
...
domain = domain.com
...
timezone = local
...
admin@server:/Users/admin$
@@ -542,9 +545,12 @@ Created: C:\GAMConfig
Created: C:\GAMConfig\gamcache
Config File: C:\GAMConfig\gam.cfg, Initialized
Section: DEFAULT
activity_max_results = 100
...
[long list of all config settings that should match the directories you specified]
cache_dir = C:\GAMConfig\gamcache
...
config_dir = C:\GAMConfig
...
drive_dir = C:\GAMWork
...
C:\>
@@ -920,9 +926,12 @@ Default Language: en
C:\>gam config customer_id C01234567 domain domain.com timezone local save verify
Config File: C:\GAMConfig\gam.cfg, Saved
Section: DEFAULT
activity_max_results = 100
...
[long list of all config settings that should match the directories you specified]
customer_id = C01234567
...
domain = domain.com
...
timezone = local
...
C:\>

View File

@@ -1,7 +1,7 @@
# Updating GAMADV-XTD3
Use these steps to update your version of GAMADV-XTD3.
- [Downloads](Downloads)
- [Downloads-Installs](Downloads-Installs)
- [Linux and MacOS and Google Cloud Shell](#linux-and-mac-os-and-google-cloud-shell)
- [Windows](#windows)
- [GAM Configuration](gam.cfg)
@@ -13,7 +13,7 @@ Use these steps to update your version of GAMADV-XTD3.
This example assumes that GAMADV-XTD3 has been installed in /Users/admin/bin/gamadv-xtd3.
If you've installed GAMADV-XTD3 in another directory, substitute that value in the directions when downloading.
See: [Downloads](Downloads)
See: [Downloads-Installs](Downloads-Installs)
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
actual email adddress.
@@ -301,7 +301,7 @@ admin@server:/Users/admin/bin/gamadv-xtd3$
This example assumes that GAMADV-XTD3 has been installed in C:\GAMADV-XTD3.
If you've installed GAMADV-XTD3 in another directory, substitute that value in the directions when downloading.
See: [Downloads](Downloads)
See: [Downloads-Installs](Downloads-Installs)
In these examples, your Google Super admin is shown as admin@domain.com; replace with the
actual email adddress.

View File

@@ -27,7 +27,7 @@ substitute that value in the directions.
Make the directory:
```
mkdir -p /Users/admin/GAMconfig
mkdir -p /Users/admin/GAMConfig
```
Add the following line:
@@ -47,7 +47,10 @@ Issue the following command replacing `<Filename>` with the name of the file you
source <Filename>
```
* Make the /Users/admin/GAMConfig directory before proceeding.
You need to make sure the GAM configuration directory actually exists. Test that like this:
```
ls -l $GAMCFGDIR
```
### Set a working directory
@@ -56,10 +59,15 @@ data in this folder and execute GAM commands from this folder. You should not us
/Users/admin/bin/gamadv-xtd3 or /Users/admin/GAMConfig for this purpose.
This example assumes that the GAM working directory will be /Users/admin/GAMWork; If you've chosen
another directory, substitute that value in the directions.
* Make the /Users/admin/GAMWork directory before proceeding.
Make the directory:
```
mkdir -p /Users/admin/GAMWork
```
### Set an alias
You should set an alias to point to /Users/admin/bin/gamadv-xtd3/gam so you can operate from the /Users/admin/GAMWork directory.
Aliases aren't available in scripts, so you may want to set a symlink instead, see below.
Add the following line:
```
@@ -91,6 +99,12 @@ Issue the following command replacing `<Filename>` with the name of the file you
source <Filename>
```
### Set a symlink
Set a symlink in `/usr/local/bin` (or some other location on $PATH) to point to GAM.
```
ln -s "/Users/admin/bin/gamadv-xtd3/gam" /usr/local/bin/gam
```
Set environment variable OLDGAMPATH to point to the existing Gam directory; /Users/admin/bin/gam will be used in this example.
If your existing Gam is in another directory, substitute that value in the directions.
```
@@ -113,122 +127,13 @@ Copied: /Users/admin/bin/gam/oauth2.txt, To: /Users/admin/GAMConfig/oauth2.txt
Copied: /Users/admin/bin/gam/client_secrets.json, To: /Users/admin/GAMConfig/client_secrets.json
Config File: /Users/admin/GAMConfig/gam.cfg, Initialized
Section: DEFAULT
activity_max_results = 100
admin_email = ''
api_calls_rate_check = false
api_calls_rate_limit = 100
api_calls_tries_limit = 10
auto_batch_min = 0
bail_on_internal_error_tries = 2
batch_size = 50
cacerts_pem = ''
...
cache_dir = /Users/admin/GAMConfig/gamcache
cache_discovery_only = true
channel_customer_id = ''
charset = utf-8
classroom_max_results = 0
client_secrets_json = client_secrets.json ; /Users/admin/GAMConfig/client_secrets.json
clock_skew_in_seconds = 10
cmdlog = ''
cmdlog_max_backups = 5
cmdlog_max_kilo_bytes = 1000
...
config_dir = /Users/admin/GAMConfig
contact_max_results = 100
csv_input_column_delimiter = ,
csv_input_quote_char = '"'
csv_input_row_drop_filter = ''
csv_input_row_drop_filter_mode = anymatch
csv_input_row_filter = ''
csv_input_row_filter_mode = allmatch
csv_input_row_limit = 0
csv_output_column_delimiter = ,
csv_output_convert_cr_nl = false
csv_output_field_delimiter = ' '
csv_output_header_drop_filter = ''
csv_output_header_filter = ''
csv_output_header_force = ''
csv_output_line_terminator = lf
csv_output_quote_char = '"'
csv_output_row_drop_filter = ''
csv_output_row_drop_filter_mode = anymatch
csv_output_row_filter = ''
csv_output_row_filter_mode = allmatch
csv_output_row_limit = 0
csv_output_subfield_delimiter = '.'
csv_output_timestamp_column = ''
csv_output_users_audit = false
customer_id = my_customer
debug_level = 0
device_max_results = 200
domain = ''
...
drive_dir = /Users/admin/GAMWork
drive_max_results = 1000
drive_v3_native_names = true
email_batch_size = 50
enable_dasa = false
event_max_results = 250
extra_args = ''
inter_batch_wait = 0
license_max_results = 100
license_skus = ''
member_max_results = 200
message_batch_size = 50
message_max_results = 500
mobile_max_results = 100
multiprocess_pool_limit = 0
never_time = Never
no_browser = false
no_cache = false
no_update_check = true
no_verify_ssl = false
num_tbatch_threads = 2
num_threads = 5
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
people_max_results = 100
print_agu_domains = ''
print_cros_ous = ''
print_cros_ous_and_children = ''
process_wait_limit = 0
quick_cros_move = false
quick_info_user = false
reseller_id = ''
retry_api_service_not_available = false
section = ''
show_api_calls_retry_data = false
show_commands = false
show_convert_cr_nl = false
show_counts_min = 1
show_gettings = true
show_gettings_got_nl = false
show_multiprocess_info = false
smtp_fqdn = ''
smtp_host = ''
smtp_password = ''
smtp_username = ''
timezone = utc
tls_max_version = ''
tls_min_version = 'TLSv1_2'
todrive_clearfilter = false
todrive_clientaccess = false
todrive_conversion = true
todrive_localcopy = false
todrive_locale = ''
todrive_nobrowser = false
todrive_noemail = true
todrive_parent = root
todrive_sheet_timeformat = ''
todrive_sheet_timestamp = false
todrive_timeformat = ''
todrive_timestamp = false
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500
user_service_account_access_only = false
...
admin@server:/Users/admin$
```
@@ -346,7 +251,7 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin$ gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.77.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -1018,7 +923,7 @@ writes the credentials into the file oauth2.txt.
C:\>del C:\GAMConfig\oauth2.txt
C:\>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.77.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
Windows-10-10.0.17134 AMD64

View File

@@ -15,6 +15,7 @@
- [Print organizational units](#print-organizational-units)
- [Display organizational unit counts](#display-organizational-unit-counts)
- [Display indented organizational unit tree](#display-indented-organizational-unit-tree)
- [Check organizational unit for contained items](#check-organizational-unit-for-contained-items)
- [Special case handling for large number of organizational units](#special-case-handling-for-large-number-of-organizational-units)
## API documentation
@@ -270,6 +271,44 @@ gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
By default, Gam displays the organizational unit tree starting at /.
* `fromparent <OrgUnitItem>` - Display the organizational unit tree starting at `<OrgUnitItem>`.
## Check organizational unit for contained items
An organizational unit can be deleted only when it contains no items:
* Chrome Browsers
* ChromeOS Devices
* Shared Drives
* Sub Org Units
* Users
This command counts those items and displays a CSV file with the item counts.
* All counts are zero - A return code of 0 is returned and the `empty` column is `True`
* Some count is greater than 0 - A return code of 25 is returned and the `empty` column is `False`
Only items directly within the OU are counted, items in sub-OUs are not counted.
```
<OrgUnitCheckName> ::=
browsers|
devices|
shareddrives|
subous|
users
<OrgUnitCheckNameList> ::= "<OrgUnitCheckName>(,<OrgUnitCheckName>)*"
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[formatjson [quotechar <Character>]]
```
By default, GAM checks each of the five items; you can select specfic fields
with `<OrgUnitCheckName>*` or `fields <OrgUnitCheckNameList>`.
By default, GAM displays the information as columns of fields; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Special case handling for large number of organizational units
By default, the `print orgs` and `show orgtree` commands issue a single API call to get the

View File

@@ -509,7 +509,7 @@ Find all the organizers and file organizers on the Golgafrincham shared drive in
```
By default, all Shared Drives specified are displayed; use the following option to select a subset of those Shared Drives.
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives; all ACLs are displayed for the selected Shared Drives
By default, all ACLS are displayed; use the following option to select a subset of the ACLS to display.
* `<PermissionMatch>* [<PermissionMatchAction>]` - Use permission matching to display a subset of the ACLs for each Shared Drive; this only applies when `pmselect` is not specified
@@ -548,7 +548,7 @@ By default, all Shared Drives are displayed; use the following options to select
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
* `matchname <RegularExpression>` - Retrieve Shared Drives with names that match a pattern.
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives; all ACLs are displayed for the selected Shared Drives
By default, Shared Drives with no permissions are not displayed; use the `shownopermissionsdrives` to control whether
Shared Drives with no permissions are displayed.

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.77.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information
```
gam version timeoffset
GAMADV-XTD3 6.77.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
Print the current version of Gam with extended details and SSL information
```
gam version extended
GAMADV-XTD3 6.77.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Version Check:
Current: 5.35.08
Latest: 6.77.14
Latest: 6.78.00
echo $?
1
```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.77.14
6.78.00
```
In Linux/MacOS you can do:
```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.77.14 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.78.00 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64

View File

@@ -3277,15 +3277,22 @@ gam info domain [<DomainName>]
[formatjson]
gam print domains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
gam show domains [formatjson]
[showitemcountonly]
gam show domains
[formatjson]
[showitemcountonly]
gam create|add domainalias|aliasdomain <DomainAlias> <DomainName>
gam delete domainalias|aliasdomain <DomainAlias>
gam info domainalias|aliasdomain <DomainAlias> [formatjson]
gam info domainalias|aliasdomain <DomainAlias>
[formatjson]
gam print domainaliases|aliasdomains [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
gam show domainaliases|aliasdomains [formatjson]
[showitemcountonly]
gam show domainaliases|aliasdomains
[formatjson]
[showitemcountonly]
# Domain - Contacts and Global Address List
@@ -4210,6 +4217,18 @@ gam print orgs|ous [todrive <ToDriveAttribute>*]
[showitemcountonly]
gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
<OrgUnitCheckName> ::=
browsers|
devices|
shareddrives|
subous|
users
<OrgUnitCheckNameList> ::= "<OrgUnitCheckName>(,<OrgUnitCheckName>)*"
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[formatjson [quotechar <Character>]]
# Printers
<PrinterAttribute> ::=

View File

@@ -2,6 +2,32 @@
Merged GAM-Team version
6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU
as it must not contain any items in order to be deleted.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#check-organizational-unit-for-contained-items
6.77.18
Added option `showitemcountonly` to `gam print domainaliases` that causes GAM to display the
number of domain aliasess on stdout; no CSV file is written.
6.77.17
Added option `showitemcountonly` to `gam print domains` that causes GAM to display the
number of domains on stdout; no CSV file is written.
6.77.16
Fixed bug in `gam <UserTypeEntity> print filelist` that caused a trap.
6.77.15
Updated `gam calendars <CalendarEntity> import event icaluid <iCalUID> json <JSONdata>` to handle API
constraints on recurring events.
6.77.14
Fixed bug in `gam calendars <CalendarEntity> import event icaluid <iCalUID> json <JSONdata>` that caused an error.

View File

@@ -332,6 +332,7 @@ ENTITY_IS_A_USER_ALIAS_RC = 21
ENTITY_IS_A_GROUP_RC = 22
ENTITY_IS_A_GROUP_ALIAS_RC = 23
ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC = 24
ORGUNIT_NOT_EMPTY_RC = 25
CHECK_USER_GROUPS_ERROR_RC = 29
ORPHANS_COLLECTED_RC = 30
# Warnings/Errors
@@ -8890,14 +8891,6 @@ def getTodriveOnly(csvPF):
else:
unknownArgumentExit()
def getTodriveFJQCOnly(csvPF, FJQC, addTitle=False, noExit=False):
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
else:
FJQC.GetFormatJSONQuoteChar(myarg, addTitle, noExit)
DEFAULT_SKIP_OBJECTS = {'kind', 'etag', 'etags', '@type'}
# Clean a JSON object
@@ -15763,18 +15756,33 @@ def _printDomain(domain, csvPF):
DOMAIN_ALIAS_SORT_TITLES = ['domainAliasName', 'parentDomainName', 'creationTime', 'verified']
# gam print domainaliases [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
# gam show domainaliases [formatjson]
# gam print domainaliases [todrive <ToDriveAttribute>*]
# [formatjson [quotechar <Character>]]
# [showitemcountonly]
# gam show domainaliases
# [formatjson]
# [showitemcountonly]
def doPrintShowDomainAliases():
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['domainAliasName'], DOMAIN_ALIAS_SORT_TITLES) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF)
getTodriveFJQCOnly(csvPF, FJQC, True)
showItemCountOnly = False
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg == 'showitemcountonly':
showItemCountOnly = True
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
try:
domainAliases = callGAPIitems(cd.domainAliases(), 'list', 'domainAliases',
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
customer=GC.Values[GC.CUSTOMER_ID])
count = len(domainAliases)
if showItemCountOnly:
writeStdout(f'{count}\n')
return
i = 0
for domainAlias in domainAliases:
i += 1
@@ -16090,18 +16098,33 @@ def doInfoDomain():
DOMAIN_SORT_TITLES = ['domainName', 'parentDomainName', 'creationTime', 'type', 'verified']
# gam print domains [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
# gam show domains [formatjson]
# gam print domains [todrive <ToDriveAttribute>*]
# [formatjson [quotechar <Character>]]
# [showitemcountonly]
# gam show domains
# [formatjson]
# [showitemcountonly]
def doPrintShowDomains():
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['domainName'], DOMAIN_SORT_TITLES) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF)
getTodriveFJQCOnly(csvPF, FJQC, True)
showItemCountOnly = False
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg == 'showitemcountonly':
showItemCountOnly = True
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
try:
domains = callGAPIitems(cd.domains(), 'list', 'domains',
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
customer=GC.Values[GC.CUSTOMER_ID])
count = len(domains)
if showItemCountOnly:
writeStdout(f'{count}\n')
return
i = 0
for domain in domains:
i += 1
@@ -17462,7 +17485,11 @@ def _getOrgUnits(cd, orgUnitPath, fieldsList, listType, showParent, batchSubOrgs
else:
fields = ','.join(set(fieldsList))
listfields = f'organizationUnits({fields})'
printGettingAllAccountEntities(Ent.ORGANIZATIONAL_UNIT)
if listType == 'all' and orgUnitPath == '/':
printGettingAllAccountEntities(Ent.ORGANIZATIONAL_UNIT)
else:
printGettingAllEntityItemsForWhom(Ent.CHILD_ORGANIZATIONAL_UNIT, orgUnitPath,
qualifier=' (Direct Children)' if listType == 'children' else '', entityType=Ent.ORGANIZATIONAL_UNIT)
if listType == 'children':
batchSubOrgs = False
try:
@@ -17500,7 +17527,10 @@ def _getOrgUnits(cd, orgUnitPath, fieldsList, listType, showParent, batchSubOrgs
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError,
GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired):
pass
printGotAccountEntities(len(orgUnits))
if listType == 'all' and orgUnitPath == '/':
printGotAccountEntities(len(orgUnits))
else:
printGotEntityItemsForWhom(len(orgUnits))
if childSelector is not None:
for orgUnit in orgUnits:
orgUnit[ORG_UNIT_SELECTOR_FIELD] = childSelector if orgUnit['orgUnitPath'] != orgUnitPath else parentSelector
@@ -17733,6 +17763,157 @@ def doShowOrgTree():
for org in sorted(orgTree):
printOrgUnit(org, orgTree)
ORG_ITEMS_FIELD_MAP = {
'browsers': 'browsers',
'devices': 'devices',
'shareddrives': 'sharedDrives',
'subous': 'subOus',
'users': 'users',
}
# gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
# [<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
# [formatjson [quotechar <Character>]]
def doCheckOrgUnit():
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['orgUnitPath', 'orgUnitId', 'empty'])
FJQC = FormatJSONQuoteChar(csvPF)
orgUnitPath = None
fieldsList = []
titlesList = []
status, orgUnitPath, orgUnitId = checkOrgUnitPathExists(cd, getOrgUnitItem())
orgUnitPathLower = orgUnitPath.lower()
if not status:
entityDoesNotExistExit(Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg in ORG_ITEMS_FIELD_MAP:
fieldsList.append(myarg)
elif myarg == 'fields':
for field in _getFieldsList():
if field in ORG_ITEMS_FIELD_MAP:
fieldsList.append(field)
else:
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
if orgUnitPath is None:
missingArgumentExit('orgunit <OrgUnitItem>')
if not fieldsList:
fieldsList = ORG_ITEMS_FIELD_MAP.keys()
orgUnitItemCounts = {}
for field in sorted(fieldsList):
title = ORG_ITEMS_FIELD_MAP[field]
orgUnitItemCounts[title] = 0
if not FJQC.formatJSON:
titlesList.append(title)
if 'browsers' in fieldsList:
cbcm = buildGAPIObject(API.CBCM)
customerId = _getCustomerIdNoC()
printGettingAllEntityItemsForWhom(Ent.CHROME_BROWSER, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessage()
try:
feed = yieldGAPIpages(cbcm.chromebrowsers(), 'list', 'browsers',
pageMessage=pageMessage, messageAttribute='deviceId',
throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.INVALID_ORGUNIT, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
customer=customerId, orgUnitPath=orgUnitPath, projection='BASIC',
fields='nextPageToken,browsers(deviceId)')
for browsers in feed:
orgUnitItemCounts['browsers'] += len(browsers)
except (GAPI.invalidInput, GAPI.forbidden) as e:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
except GAPI.invalidOrgunit as e:
entityActionFailedExit([Ent.CHROME_BROWSER, None], str(e))
except (GAPI.badRequest, GAPI.resourceNotFound):
accessErrorExit(None)
if 'devices' in fieldsList:
printGettingAllEntityItemsForWhom(Ent.CROS_DEVICE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessageForWhom()
pageToken = None
totalItems = 0
tokenRetries = 0
while True:
try:
feed = callGAPI(cd.chromeosdevices(), 'list',
throwReasons=[GAPI.INVALID_INPUT, GAPI.INVALID_ORGUNIT,
GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
pageToken=pageToken, customerId=GC.Values[GC.CUSTOMER_ID],
orgUnitPath=orgUnitPath, fields='nextPageToken,chromeosdevices(deviceId)', maxResults=GC.Values[GC.DEVICE_MAX_RESULTS])
tokenRetries = 0
pageToken, totalItems = _processGAPIpagesResult(feed, 'chromeosdevices', None, totalItems, pageMessage, None, Ent.CROS_DEVICE)
if feed:
orgUnitItemCounts['devices'] += len(feed.get('chromeosdevices', []))
del feed
if not pageToken:
_finalizeGAPIpagesResult(pageMessage)
printGotAccountEntities(totalItems)
break
except GAPI.invalidInput as e:
message = str(e)
# Invalid Input: xyz - Check for invalid pageToken!!
# 0123456789012345
if message[15:] == pageToken:
tokenRetries += 1
if tokenRetries <= 2:
writeStderr(f'{WARNING_PREFIX}{Msg.LIST_CHROMEOS_INVALID_INPUT_PAGE_TOKEN_RETRY}')
time.sleep(tokenRetries*5)
continue
entityActionFailedWarning([Ent.CROS_DEVICE, None], message)
break
entityActionFailedWarning([Ent.CROS_DEVICE, None], message)
break
except GAPI.invalidOrgunit as e:
entityActionFailedExit([Ent.CROS_DEVICE, None], str(e))
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
accessErrorExit(cd)
if 'shareddrives' in fieldsList:
ci = buildGAPIObject(API.CLOUDIDENTITY_ORGUNITS_BETA)
printGettingAllEntityItemsForWhom(Ent.SHAREDDRIVE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
sds = callGAPIpages(ci.orgUnits().memberships(), 'list', 'orgMemberships',
pageMessage=getPageMessageForWhom(),
parent=f'orgUnits/{orgUnitId[3:]}',
customer=_getCustomersCustomerIdWithC(),
filter="type == 'shared_drive'")
orgUnitItemCounts['sharedDrives'] = len(sds)
if 'subous' in fieldsList:
orgUnitItemCounts['subOus'] = len(_getOrgUnits(cd, orgUnitPath, ['orgUnitPath'], 'children', False, False, None, None))
if 'users' in fieldsList:
printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessageForWhom()
try:
feed = yieldGAPIpages(cd.users(), 'list', 'users',
pageMessage=pageMessage,
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None),
fields='nextPageToken,users(orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
for users in feed:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
orgUnitItemCounts['users'] += 1
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError,
GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
empty = True
for count in orgUnitItemCounts.values():
if count > 0:
empty = False
break
baseRow = {'orgUnitPath': orgUnitPath, 'orgUnitId': orgUnitId, 'empty': empty}
row = flattenJSON(orgUnitItemCounts, baseRow.copy())
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
baseRow['JSON'] = json.dumps(cleanJSON(orgUnitItemCounts), ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(baseRow)
csvPF.writeCSVfile(f'OrgUnit {orgUnitPath} Item Counts')
if not empty and GM.Globals[GM.SYSEXITRC] == 0:
setSysExitRC(ORGUNIT_NOT_EMPTY_RC)
ALIAS_TARGET_TYPES = ['user', 'group', 'target']
# gam create aliases|nicknames <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
@@ -25035,10 +25216,10 @@ def doPrintShowBrowsers():
else:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
return
except GAPI.invalidOrgunit as e:
except (GAPI.invalidOrgunit, GAPI.forbidden) as e:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
return
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
except (GAPI.badRequest, GAPI.resourceNotFound):
accessErrorExit(None)
else:
sortRows = True
@@ -36918,9 +37099,13 @@ def _getCalendarEventAttribute(myarg, body, parameters, function):
parameters['attendees'].append(addAttendee)
elif myarg == 'json':
jsonData = getJSON(EVENT_JSON_CLEAR_FIELDS)
if function in {'insert', 'import'}:
if function == 'insert':
body.update(jsonData)
clearJSONfields(body, EVENT_JSON_INSERT_CLEAR_FIELDS)
elif function == 'import':
if 'id' in jsonData:
jsonData['iCalUID'] = jsonData.pop('id')
body.update(jsonData)
elif function == 'update':
if 'event' in jsonData and 'attendees' in jsonData['event']:
parameters['attendees'].extend(jsonData['event'].pop('attendees'))
@@ -43787,18 +43972,21 @@ def doPrintUsers(entityList=None):
projection=schemaParms['projection'], customFieldMask=schemaParms['customFieldMask'],
maxResults=maxResults, **kwargs)
for users in feed:
if showItemCountOnly:
itemCount += len(users)
continue
if orgUnitPath is None:
if not printOptions['countOnly']:
if showItemCountOnly:
itemCount += len(users)
elif not printOptions['countOnly']:
for user in users:
_printUser(user, 0, 0)
else:
for user in users:
_updateDomainCounts(user['primaryEmail'])
else:
if not printOptions['countOnly']:
if showItemCountOnly:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
itemCount += 1
elif not printOptions['countOnly']:
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
_printUser(user, 0, 0)
@@ -54409,7 +54597,7 @@ def printFileList(users):
simpleLists = ['permissionIds', 'spaces']
skipObjects = set()
fileIdEntity = {}
selectSubQuery = ''
getSharedDriveACLsCountMsg = selectSubQuery = ''
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
DLP = DriveListParameters({'allowChoose': True, 'allowCorpora': True, 'allowQuery': True, 'mimeTypeInQuery': False})
DFF = DriveFileFields()
@@ -63834,7 +64022,9 @@ def doPrintShowOrgunitSharedDrives():
if csvPF and FJQC.formatJSON:
csvPF.SetJSONTitles(['name', 'JSON'])
_, orgUnitId = getOrgUnitId(None, orgUnitPath)
printGettingAllEntityItemsForWhom(Ent.SHAREDDRIVE, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
sds = callGAPIpages(ci.orgUnits().memberships(), 'list', 'orgMemberships',
pageMessage=getPageMessageForWhom(),
parent=f'orgUnits/{orgUnitId[3:]}',
customer=_getCustomersCustomerIdWithC(),
filter="type == 'shared_drive'")
@@ -73647,6 +73837,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
{Cmd.ARG_SVCACCT: doCheckUpdateSvcAcct,
Cmd.ARG_USERINVITATION: doCheckCIUserInvitations,
Cmd.ARG_ISINVITABLE: doCheckCIUserInvitations,
Cmd.ARG_ORG: doCheckOrgUnit,
}
),
'clear':

View File

@@ -94,6 +94,7 @@ class GamEntity():
CHAT_MESSAGE_ID = 'chmi'
CHAT_SPACE = 'chsp'
CHAT_THREAD = 'chth'
CHILD_ORGANIZATIONAL_UNIT = 'corg'
CHROME_APP = 'capp'
CHROME_APP_DEVICE = 'capd'
CHROME_BROWSER = 'chbr'
@@ -435,6 +436,7 @@ class GamEntity():
CHAT_MEMBER_USER: ['Chat User Members', 'Chat User Member'],
CHAT_SPACE: ['Chat Spaces', 'Chat Space'],
CHAT_THREAD: ['Chat Threads', 'Chat Thread'],
CHILD_ORGANIZATIONAL_UNIT: ['Child Organizational Units', 'Child Organizational Unit'],
CHROME_APP: ['Chrome Applications', 'Chrome Application'],
CHROME_APP_DEVICE: ['Chrome Application Devices', 'Chrome Application Device'],
CHROME_BROWSER: ['Chrome Browsers', 'Chrome Browser'],