Compare commits

..

29 Commits

Author SHA1 Message Date
Jay Lee
e4af5e6126 [no ci] Enhance build workflow with custom cryptography wheels
Added conditional installation for custom cryptography wheels on macOS and Windows ARM64.
2026-06-27 15:23:10 -04:00
Jay Lee
6e296e0f2d Clean up build.yml by removing ykman hack
Removed workaround for ykman cryptography dependency.
2026-06-27 15:16:07 -04:00
github-actions[bot]
4b2e14c2d5 chore: upgrade PyPi deps (#1933)
Co-authored-by: jay0lee <4623536+jay0lee@users.noreply.github.com>
2026-06-27 15:13:30 -04:00
Jay Lee
eb59663f6a [no ci] Enhance upgrade_deps.yml with comments and cleanup
Updated comments for clarity and added cleanup for resolved.txt.
2026-06-27 15:10:29 -04:00
Jay Lee
d12289c4f4 [no ci] Add uv installation and enhance dependency resolution
This update adds a step to install the 'uv' package and modifies the dependency resolution process to handle mutually compatible package versions. It also updates the commit message body for clarity.
2026-06-27 15:06:38 -04:00
Jay Lee
738ff3e7fb Modify ykman installation to remove cryptography constraint
Updated the build workflow to patch the ykman METADATA file for cryptography dependency.
2026-06-27 14:51:37 -04:00
Jay Lee
789e543b3f Fix yubikey-manager installation and cryptography dependency
Updated yubikey-manager installation to use version 5.9.1 and modified cryptography dependency handling.
2026-06-27 14:34:01 -04:00
Jay Lee
4e933f4485 Modify yubikey installation commands in build.yml
Updated installation commands for yubikey-manager and dependencies.
2026-06-27 14:11:37 -04:00
Jay Lee
a99f23ce40 Refactor pip installation steps in build workflow
Commented out pip upgrade commands and adjusted yubikey installation.
2026-06-27 14:09:10 -04:00
github-actions[bot]
5462c0359e chore: upgrade PyPi deps (#1931)
Co-authored-by: jay0lee <4623536+jay0lee@users.noreply.github.com>
2026-06-27 14:06:15 -04:00
Jay Lee
0c143414a8 Delete dep-overrides.txt 2026-06-27 14:01:08 -04:00
Jay Lee
bebcfb7c44 Update dependency overrides in dep-overrides.txt 2026-06-27 13:58:00 -04:00
Jay Lee
18ce64886d Modify yubikey-manager installation command
Update yubikey-manager installation to avoid dependency installation.
2026-06-27 13:32:54 -04:00
Jay Lee
db87aa54d8 Update yubikey-manager installation to version 5.9.1
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 x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, 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, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Specify version for yubikey-manager installation
2026-06-27 13:28:14 -04:00
Jay Lee
1867af9366 [actions] Comment out yubikey upgrade command in build.yml
Comment out the upgrade command for yubikey dependency and add note about future upgrade.
2026-06-27 13:18:54 -04:00
github-actions[bot]
e1f2bedd7c chore: upgrade PyPi deps (#1930)
Co-authored-by: jay0lee <4623536+jay0lee@users.noreply.github.com>
2026-06-27 13:01:09 -04:00
Ross Scroggs
8735021c9b Update Vault API error handling 2026-06-27 09:16:54 -07:00
Ross Scroggs
2860ae02a2 Update Users-People-Contacts-Profiles.md
Some checks failed
Push wiki / pushwiki (push) Has been cancelled
2026-06-26 19:26:16 -07:00
github-actions[bot]
31b4b70f0f chore: upgrade PyPi deps (#1929)
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 x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, 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, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
Daily Dependency Pinning (2-Week Buffer) / pin-deps (push) Has been cancelled
Co-authored-by: jay0lee <4623536+jay0lee@users.noreply.github.com>
2026-06-25 05:09:11 -04:00
Jay Lee
1c8bf44867 Update checkout action to version 7.0.0
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 x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, 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, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
Daily Dependency Pinning (2-Week Buffer) / pin-deps (push) Has been cancelled
2026-06-23 11:33:08 -04:00
Jay Lee
472905d6c0 [no ci] Update checkout action to version 7.0.0 2026-06-23 11:32:46 -04:00
Jay Lee
bed9b5e1b6 [no ci] Update checkout action version in pushwiki.yml 2026-06-23 11:32:19 -04:00
Jay Lee
c8521c6307 [no ci] Update checkout action to version 7.0.0 2026-06-23 11:31:48 -04:00
Jay Lee
870a3149d8 [no ci] Upgrade actions/checkout to version 7.0.0
Updated checkout action version from v5.0.0 to v7.0.0.
2026-06-23 11:31:07 -04:00
Jay Lee
8622ae6c0f Upgrade actions/checkout to version 7.0.0
Updated checkout action version from v6.0.2 to v7.0.0 in build workflows.
2026-06-23 11:30:33 -04:00
Jay Lee
c92468276a [actions] reduce pip verbosity
Remove verbose flag from pip install command for yubikey.
2026-06-23 05:25:54 -04:00
dependabot[bot]
d80b93b86e Bump cryptography in the pip group across 1 directory (#1926)
Bumps the pip group with 1 update in the / directory: [cryptography](https://github.com/pyca/cryptography).


Updates `cryptography` from 48.0.0 to 48.0.1
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/48.0.0...48.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 48.0.1
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 04:53:05 -04:00
Ross Scroggs
2d26fa6004 Update GamUpdates.md
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 x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, 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, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
Daily Dependency Pinning (2-Week Buffer) / pin-deps (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
2026-06-19 08:10:22 -07:00
Ross Scroggs
a09a98e3ae Updated gam <UserTypeEntity> show calsettings 2026-06-19 08:00:34 -07:00
15 changed files with 225 additions and 197 deletions

View File

@@ -145,7 +145,7 @@ jobs:
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
@@ -529,18 +529,6 @@ jobs:
echo "gam=${gam}" >> $GITHUB_ENV echo "gam=${gam}" >> $GITHUB_ENV
fi fi
#- name: Upgrade pip, wheel, etc
# run: |
# curl $curl_retry -O https://bootstrap.pypa.io/get-pip.py
# "$PYTHON" get-pip.py
# "$PYTHON" -m pip install --upgrade pip
# "$PYTHON" -m pip install --upgrade wheel
# "$PYTHON" -m pip install --upgrade setuptools
# "$PYTHON" -m pip install --upgrade importlib-metadata
# "$PYTHON" -m pip install --upgrade setuptools-scm
# "$PYTHON" -m pip install --upgrade packaging
# "$PYTHON" -m pip list
- name: Install pip requirements - name: Install pip requirements
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
@@ -550,8 +538,12 @@ jobs:
# https://github.com/pyca/cryptography/issues/14293 # https://github.com/pyca/cryptography/issues/14293
gh release download --repo "jay0lee/cryptography-wheels" --pattern "*win_arm64.whl" --clobber gh release download --repo "jay0lee/cryptography-wheels" --pattern "*win_arm64.whl" --clobber
"$PYTHON" -m pip install cryptography-*.whl "$PYTHON" -m pip install cryptography-*.whl
elif [[ "$RUNNER_OS" == "macOS" && "$RUNNER_ARCH" == "x86_64" ]]; then
# custom cryptography wheel for macos x86_64 since it's no longer standard
gh release download --repo "jay0lee/cryptography-wheels" --pattern "*macosx_15_0_x86_64.whl" --clobber
"$PYTHON" -m pip install cryptography-*.whl
fi fi
"$PYTHON" -m pip install -vvv --upgrade ..[yubikey] "$PYTHON" -m pip install ..[yubikey]
- name: Install PyInstaller - name: Install PyInstaller
if: matrix.goal == 'build' if: matrix.goal == 'build'
@@ -1121,7 +1113,7 @@ jobs:
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0

View File

@@ -39,7 +39,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v3
with: with:

View File

@@ -14,7 +14,7 @@ jobs:
check-certs: check-certs:
runs-on: ubuntu-slim runs-on: ubuntu-slim
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo fetch-depth: 0 # otherwise, you will failed to push refs to dest repo

View File

@@ -18,7 +18,7 @@ jobs:
git clone https://github.com/GAM-team/GAM git clone https://github.com/GAM-team/GAM
- name: Checkout Wiki source - name: Checkout Wiki source
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
path: GAM.wiki path: GAM.wiki
repository: GAM-team/GAM.wiki repository: GAM-team/GAM.wiki

View File

@@ -16,7 +16,7 @@ jobs:
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0

View File

@@ -15,20 +15,25 @@ jobs:
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
with: with:
python-version: '3.14' python-version: '3.14'
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Calculate and pin two-week old stable versions - name: Calculate and pin two-week old stable versions
shell: python shell: python
run: | run: |
import json import subprocess
import urllib.request
import re import re
import tomllib import tomllib
import os
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
@@ -42,14 +47,10 @@ jobs:
target_deps = set() target_deps = set()
# Standard [project.dependencies] # Gather all dependencies
target_deps.update(parsed_toml.get("project", {}).get("dependencies", [])) target_deps.update(parsed_toml.get("project", {}).get("dependencies", []))
# Optional [project.optional-dependencies]
for deps in parsed_toml.get("project", {}).get("optional-dependencies", {}).values(): for deps in parsed_toml.get("project", {}).get("optional-dependencies", {}).values():
target_deps.update(deps) target_deps.update(deps)
# Dev [dependency-groups] (uv / PEP 735 standard)
for deps in parsed_toml.get("dependency-groups", {}).values(): for deps in parsed_toml.get("dependency-groups", {}).values():
if isinstance(deps, list): if isinstance(deps, list):
for d in deps: for d in deps:
@@ -60,73 +61,80 @@ jobs:
print("No dependencies found to process.") print("No dependencies found to process.")
exit(0) exit(0)
updates = {} # 1. Create a "clean" requirements list (base names + markers only, no versions)
two_weeks_ago = datetime.now(timezone.utc) - timedelta(days=14) clean_reqs = []
print(f"Evaluating dependencies against cutoff date: {two_weeks_ago.isoformat()}")
for dep in target_deps: for dep in target_deps:
# Isolate base package name (e.g., "yubikey-manager>=5.6.1" -> "yubikey-manager") pkg_base = re.split(r'[<>=!~;\s]', dep)[0].strip()
pkg_name = re.split(r'[<>=!~;\s]', dep)[0].strip() if ";" in dep:
marker = dep.split(";", 1)[1].strip()
clean_reqs.append(f"{pkg_base} ; {marker}")
else:
clean_reqs.append(pkg_base)
temp_in = Path("temp_reqs.in")
temp_in.write_text("\n".join(clean_reqs), encoding="utf-8")
# 2. Calculate Cutoff Date
two_weeks_ago = datetime.now(timezone.utc) - timedelta(days=14)
cutoff_str = two_weeks_ago.strftime("%Y-%m-%dT%H:%M:%SZ")
print(f"Resolving dependencies against cutoff date: {cutoff_str}")
# 3. Use uv to resolve the CLEAN list, allowing free upgrades/downgrades
try:
subprocess.run([
"uv", "pip", "compile",
str(temp_in),
"--exclude-newer", cutoff_str,
"--quiet",
"-o", "resolved.txt"
], check=True)
except subprocess.CalledProcessError:
print("\nDependency resolution failed! Upstream constraints are impossible to satisfy.")
if temp_in.exists(): temp_in.unlink()
exit(1)
# 4. Parse the resolved lockfile
resolved_versions = {}
with open("resolved.txt", "r", encoding="utf-8") as f:
for line in f:
line = line.split("#")[0].strip()
if "==" in line:
pkg, ver = line.split("==", 1)
resolved_versions[pkg.strip().lower()] = ver.strip()
# Cleanup temp files so they don't get committed to the PR
if temp_in.exists():
temp_in.unlink()
if Path("resolved.txt").exists():
Path("resolved.txt").unlink()
# 5. Map the newly resolved versions back to your pyproject.toml updates
updates = {}
for dep in target_deps:
pkg_name_raw = re.split(r'[<>=!~;\s]', dep)[0].strip()
pkg_name_lower = pkg_name_raw.lower()
# Preserve environment markers if they exist
marker = "" marker = ""
if ";" in dep: if ";" in dep:
marker = " ; " + dep.split(";", 1)[1].strip() marker = " ; " + dep.split(";", 1)[1].strip()
print(f"Fetching PyPI data for: {pkg_name}") if pkg_name_lower in resolved_versions:
try: target_version = resolved_versions[pkg_name_lower]
url = f"https://pypi.org/pypi/{pkg_name}/json" pinned_dep = f"{pkg_name_raw}=={target_version}{marker}"
req = urllib.request.Request(url, headers={'User-Agent': 'GAM-CI-Script'})
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode())
valid_versions = [] if pinned_dep != dep:
for ver, files in data.get("releases", {}).items(): updates[dep] = pinned_dep
if not files: print(f" -> Changing: '{dep}' => '{pinned_dep}'")
continue
upload_time_str = files[0].get("upload_time_iso_8601")
if not upload_time_str:
continue
if upload_time_str.endswith('Z'):
upload_time_str = upload_time_str[:-1] + '+00:00'
upload_time = datetime.fromisoformat(upload_time_str)
# Filter: Must be older than 2 weeks and not a pre-release
if upload_time <= two_weeks_ago and not any(x in ver.lower() for x in ['a', 'b', 'rc', 'dev']):
valid_versions.append((upload_time, ver))
if valid_versions:
# Sort by upload time descending to get the newest valid option
valid_versions.sort(key=lambda x: x[0], reverse=True)
target_version = valid_versions[0][1]
pinned_dep = f"{pkg_name}=={target_version}{marker}"
if pinned_dep != dep:
updates[dep] = pinned_dep
print(f" -> Pinning: '{dep}' => '{pinned_dep}'")
else:
print(f" -> Already pinned correctly to {target_version}")
else: else:
print(f" -> No valid historical versions found.") print(f" -> Up to date: {dep}")
except urllib.error.HTTPError as e: # 6. Replace the strings safely in the original file content
print(f" -> Package not found on PyPI or HTTP error: {e}")
except Exception as e:
print(f" -> Error processing {pkg_name}: {e}")
# 3. Replace the strings safely in the original file content
new_content = content new_content = content
for old_dep, new_dep in updates.items(): for old_dep, new_dep in updates.items():
# Regex targets the exact string inside either single or double quotes
# Using a lambda replacement ensures we don't trip over escape sequences in the new string
escaped_old = re.escape(old_dep) escaped_old = re.escape(old_dep)
pattern = r'([\'"])' + escaped_old + r'\1' pattern = r'([\'"])' + escaped_old + r'\1'
new_content = re.sub(pattern, lambda m: m.group(1) + new_dep + m.group(1), new_content) new_content = re.sub(pattern, lambda m: m.group(1) + new_dep + m.group(1), new_content)
# Write changes back to pyproject.toml
if content != new_content: if content != new_content:
toml_path.write_text(new_content, encoding="utf-8") toml_path.write_text(new_content, encoding="utf-8")
print("\npyproject.toml updated successfully.") print("\npyproject.toml updated successfully.")
@@ -139,6 +147,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "chore: upgrade PyPi deps" commit-message: "chore: upgrade PyPi deps"
title: "Upgrade PyPi deps" title: "Upgrade PyPi deps"
body: "Automated scan checking PyPI for package versions at least 2 weeks old." body: "Automated scan checking PyPI for mutually compatible package versions at least 2 weeks old. Handles both upgrades and conflict-driven downgrades."
branch: sys-deps-upgrade branch: sys-deps-upgrade
force: false # Standard push, plays nice with rulesets force: false # Standard push, plays nice with rulesets

View File

@@ -1,6 +0,0 @@
# overrides uv.lock to force newer dependencies
# when old deps are vulnerable. These should be set
# to expire after 2 weeks when the fixed version will
# be automatically picked up anyway.
# Format: package_requirement | MM/DD/YYYY
urllib3>=2.7.0 | 05/22/2026

View File

@@ -10,13 +10,13 @@ authors = [
dependencies = [ dependencies = [
"arrow==1.4.0", "arrow==1.4.0",
"chardet==7.4.3", "chardet==7.4.3",
"cryptography==48.0.0", "cryptography==48.0.1",
"distro==1.9.0 ; sys_platform=='linux'", "distro==1.9.0 ; sys_platform=='linux'",
"filelock==3.29.1", "filelock==3.29.4",
"google-api-python-client==2.197.0", "google-api-python-client==2.197.0",
"google-auth-httplib2==0.4.0", "google-auth-httplib2==0.4.0",
"google-auth-oauthlib==1.4.0", "google-auth-oauthlib==1.4.0",
"google-auth==2.53.0", "google-auth==2.54.0",
"httplib2==0.31.2", "httplib2==0.31.2",
"lxml==6.1.1", "lxml==6.1.1",
"passlib==1.7.4", "passlib==1.7.4",

View File

@@ -1676,6 +1676,10 @@ gam <UserTypeEntity> show analyticdatastreams
<CalendarACLScopeEntity>::= <CalendarACLScopeEntity>::=
<CalendarACLScopeList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector> <CalendarACLScopeList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector>
Transfer ownership of a selection of a users secondary calendars to another user
gam calendars <CalendarEntity> transfer <UserItem>
gam calendars <CalendarEntity> create|add acls|calendaracls <CalendarACLRole> <CalendarACLScopeEntity> [sendnotifications <Boolean>] gam calendars <CalendarEntity> create|add acls|calendaracls <CalendarACLRole> <CalendarACLScopeEntity> [sendnotifications <Boolean>]
gam calendars <CalendarEntity> update acls|calendaracls <CalendarACLRole> <CalendarACLScopeEntity> [sendnotifications <Boolean>] gam calendars <CalendarEntity> update acls|calendaracls <CalendarACLRole> <CalendarACLScopeEntity> [sendnotifications <Boolean>]
gam calendars <CalendarEntity> delete acls|calendaracls [<CalendarACLRole>] <CalendarACLScopeEntity> gam calendars <CalendarEntity> delete acls|calendaracls [<CalendarACLRole>] <CalendarACLScopeEntity>
@@ -6298,13 +6302,6 @@ gam <UserTypeEntity> print calendaracls <UserCalendarEntity> [todrive <ToDriveAt
[noselfowner] (addcsvdata <FieldName> <String>)* [noselfowner] (addcsvdata <FieldName> <String>)*
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
Transfer ownership of a selection of a users secondary calendars to another user
gam <UserTypeEntity> transfer calendars|seccals <UserItem> [<UserCalendarEntity>]
[keepuser | (retainrole <CalendarACLRole>)] [sendnotifications <Boolean>] [noretentionmessages]
<CalendarSettings>* [append description|location|summary] [noupdatemessages]
[deletefromoldowner] [addtonewowner <CalendarAttribute>*] [nolistmessages]
<AttendeeAttendance> ::= optional|required <AttendeeAttendance> ::= optional|required
<AttendeeStatus> ::= accepted|declined|needsaction|tentative <AttendeeStatus> ::= accepted|declined|needsaction|tentative

View File

@@ -1,6 +1,10 @@
7.46.03
Updated all Vault related commands to handle the following error: `ERROR: 403: permissionDenied`
7.46.02 7.46.02
Updated `gam <UserTypeEntity> show calsettings` to display `dataOwner` field; Updated `gam calendars <CalendarEntity> show settings` to display `dataOwner` field;
it is labelled `Owner`. it is labelled `Owner`.
7.46.01 7.46.01

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.46.02' __version__ = '7.46.03'
__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
@@ -683,7 +683,7 @@ def accessErrorMessage(cd, errMsg=None):
[Ent.Singular(Ent.CUSTOMER_ID), GC.Values[GC.CUSTOMER_ID], [Ent.Singular(Ent.CUSTOMER_ID), GC.Values[GC.CUSTOMER_ID],
Msg.DOES_NOT_EXIST], Msg.DOES_NOT_EXIST],
'') '')
except GAPI.forbidden: except (GAPI.forbidden, GAPI.permissionDenied):
return formatKeyValueList('', return formatKeyValueList('',
Ent.FormatEntityValueList([Ent.CUSTOMER_ID, GC.Values[GC.CUSTOMER_ID], Ent.FormatEntityValueList([Ent.CUSTOMER_ID, GC.Values[GC.CUSTOMER_ID],
Ent.DOMAIN, GC.Values[GC.DOMAIN], Ent.DOMAIN, GC.Values[GC.DOMAIN],
@@ -24871,9 +24871,9 @@ def infoCrOSDevices(entityList):
i += 1 i += 1
try: try:
cros = callGAPI(cd.chromeosdevices(), 'get', cros = callGAPI(cd.chromeosdevices(), 'get',
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
customerId=GC.Values[GC.CUSTOMER_ID], deviceId=deviceId, projection=projection, fields=fields) customerId=GC.Values[GC.CUSTOMER_ID], deviceId=deviceId, projection=projection, fields=fields)
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.permissionDenied):
checkEntityAFDNEorAccessErrorExit(cd, Ent.CROS_DEVICE, deviceId, i, count) checkEntityAFDNEorAccessErrorExit(cd, Ent.CROS_DEVICE, deviceId, i, count)
continue continue
checkTPMVulnerability(cros) checkTPMVulnerability(cros)
@@ -25498,7 +25498,7 @@ def doPrintCrOSDevices(entityList=None):
try: try:
feed = callGAPI(cd.chromeosdevices(), 'list', feed = callGAPI(cd.chromeosdevices(), 'list',
throwReasons=[GAPI.INVALID_INPUT, GAPI.INVALID_ORGUNIT, throwReasons=[GAPI.INVALID_INPUT, GAPI.INVALID_ORGUNIT,
GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
pageToken=pageToken, pageToken=pageToken,
customerId=GC.Values[GC.CUSTOMER_ID], query=query, projection=projection, customerId=GC.Values[GC.CUSTOMER_ID], query=query, projection=projection,
@@ -25532,7 +25532,7 @@ def doPrintCrOSDevices(entityList=None):
except GAPI.invalidOrgunit as e: except GAPI.invalidOrgunit as e:
entityActionFailedWarning([Ent.CROS_DEVICE, None], str(e)) entityActionFailedWarning([Ent.CROS_DEVICE, None], str(e))
return return
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.permissionDenied):
accessErrorExit(cd) accessErrorExit(cd)
if showItemCountOnly: if showItemCountOnly:
writeStdout(f'{totalItems}\n') writeStdout(f'{totalItems}\n')
@@ -43644,7 +43644,7 @@ def convertExportNameToID(v, nameOrId, matterId, matterNameId):
if cg: if cg:
try: try:
export = callGAPI(v.matters().exports(), 'get', export = callGAPI(v.matters().exports(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION], GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
matterId=matterId, exportId=cg.group(1)) matterId=matterId, exportId=cg.group(1))
return (export['id'], export['name'], formatVaultNameId(export['id'], export['name'])) return (export['id'], export['name'], formatVaultNameId(export['id'], export['name']))
@@ -43652,14 +43652,14 @@ def convertExportNameToID(v, nameOrId, matterId, matterNameId):
entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId]) entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId])
except (GAPI.failedPrecondition) as e: except (GAPI.failedPrecondition) as e:
entityActionFailedExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId], str(e)) entityActionFailedExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId], str(e))
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
nameOrIdlower = nameOrId.lower() nameOrIdlower = nameOrId.lower()
try: try:
exports = callGAPIpages(v.matters().exports(), 'list', 'exports', exports = callGAPIpages(v.matters().exports(), 'list', 'exports',
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields='exports(id,name),nextPageToken') matterId=matterId, fields='exports(id,name),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
for export in exports: for export in exports:
if export['name'].lower() == nameOrIdlower: if export['name'].lower() == nameOrIdlower:
@@ -43671,19 +43671,19 @@ def convertHoldNameToID(v, nameOrId, matterId, matterNameId):
if cg: if cg:
try: try:
hold = callGAPI(v.matters().holds(), 'get', hold = callGAPI(v.matters().holds(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=cg.group(1)) matterId=matterId, holdId=cg.group(1))
return (hold['holdId'], hold['name'], formatVaultNameId(hold['holdId'], hold['name'])) return (hold['holdId'], hold['name'], formatVaultNameId(hold['holdId'], hold['name']))
except (GAPI.notFound, GAPI.badRequest): except (GAPI.notFound, GAPI.badRequest):
entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, nameOrId]) entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, nameOrId])
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
nameOrIdlower = nameOrId.lower() nameOrIdlower = nameOrId.lower()
try: try:
holds = callGAPIpages(v.matters().holds(), 'list', 'holds', holds = callGAPIpages(v.matters().holds(), 'list', 'holds',
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields='holds(holdId,name),nextPageToken') matterId=matterId, fields='holds(holdId,name),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
for hold in holds: for hold in holds:
if hold['name'].lower() == nameOrIdlower: if hold['name'].lower() == nameOrIdlower:
@@ -43695,16 +43695,18 @@ def convertMatterNameToID(v, nameOrId, state=None):
if cg: if cg:
try: try:
matter = callGAPI(v.matters(), 'get', matter = callGAPI(v.matters(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=cg.group(1), view='BASIC', fields='matterId,name,state') matterId=cg.group(1), view='BASIC', fields='matterId,name,state')
return (matter['matterId'], matter['name'], formatVaultNameId(matter['name'], matter['matterId']), matter['state']) return (matter['matterId'], matter['name'], formatVaultNameId(matter['name'], matter['matterId']), matter['state'])
except (GAPI.notFound, GAPI.forbidden): except GAPI.notFound:
entityDoesNotExistExit(Ent.VAULT_MATTER, nameOrId) entityDoesNotExistExit(Ent.VAULT_MATTER, nameOrId)
except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e))
try: try:
matters = callGAPIpages(v.matters(), 'list', 'matters', matters = callGAPIpages(v.matters(), 'list', 'matters',
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
view='BASIC', state=state, fields='matters(matterId,name,state),nextPageToken') view='BASIC', state=state, fields='matters(matterId,name,state),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
nameOrIdlower = nameOrId.lower() nameOrIdlower = nameOrId.lower()
ids = [] ids = []
@@ -43726,19 +43728,19 @@ def convertQueryNameToID(v, nameOrId, matterId, matterNameId):
if cg: if cg:
try: try:
query = callGAPI(v.matters().savedQueries(), 'get', query = callGAPI(v.matters().savedQueries(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, savedQueryId=cg.group(1)) matterId=matterId, savedQueryId=cg.group(1))
return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']), query['query']) return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']), query['query'])
except (GAPI.notFound, GAPI.badRequest): except (GAPI.notFound, GAPI.badRequest):
entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, nameOrId]) entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, nameOrId])
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
nameOrIdlower = nameOrId.lower() nameOrIdlower = nameOrId.lower()
try: try:
queries = callGAPIpages(v.matters().savedQueries(), 'list', 'savedQueries', queries = callGAPIpages(v.matters().savedQueries(), 'list', 'savedQueries',
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields='savedQueries(savedQueryId,displayName,query),nextPageToken') matterId=matterId, fields='savedQueries(savedQueryId,displayName,query),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
for query in queries: for query in queries:
if query['displayName'].lower() == nameOrIdlower: if query['displayName'].lower() == nameOrIdlower:
@@ -43753,9 +43755,9 @@ def warnMatterNotOpen(v, matter, matterNameId, j, jcount):
if v is not None: if v is not None:
try: try:
matter['state'] = callGAPI(v.matters(), 'get', matter['state'] = callGAPI(v.matters(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matter['matterId'], view='BASIC', fields='state')['state'] matterId=matter['matterId'], view='BASIC', fields='state')['state']
except (GAPI.notFound, GAPI.forbidden, GAPI.invalidArgument): except (GAPI.notFound, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument):
matter['state'] = 'Unknown' matter['state'] = 'Unknown'
else: else:
setSysExitRC(DATA_NOT_AVALIABLE_RC) setSysExitRC(DATA_NOT_AVALIABLE_RC)
@@ -44126,10 +44128,10 @@ def doDeleteVaultExport():
unknownArgumentExit() unknownArgumentExit()
try: try:
callGAPI(v.matters().exports(), 'delete', callGAPI(v.matters().exports(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, exportId=exportId) matterId=matterId, exportId=exportId)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId])
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
VAULT_EXPORT_FIELDS_CHOICE_MAP = { VAULT_EXPORT_FIELDS_CHOICE_MAP = {
@@ -44184,7 +44186,7 @@ def doInfoVaultExport():
GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION], GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
matterId=matterId, exportId=exportId, fields=fields) matterId=matterId, exportId=exportId, fields=fields)
_showVaultExport(matterNameId, export, cd, FJQC) _showVaultExport(matterNameId, export, cd, FJQC)
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
VAULT_EXPORT_STATUS_MAP = {'completed': 'COMPLETED', 'failed': 'FAILED', 'inprogress': 'IN_PROGRESS'} VAULT_EXPORT_STATUS_MAP = {'completed': 'COMPLETED', 'failed': 'FAILED', 'inprogress': 'IN_PROGRESS'}
@@ -44247,9 +44249,9 @@ def doPrintShowVaultExports():
try: try:
results = callGAPIpages(v.matters(), 'list', 'matters', results = callGAPIpages(v.matters(), 'list', 'matters',
pageMessage=getPageMessage(), pageMessage=getPageMessage(),
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken') view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_EXPORT, None], str(e)) entityActionFailedWarning([Ent.VAULT_EXPORT, None], str(e))
return return
else: else:
@@ -44277,12 +44279,12 @@ def doPrintShowVaultExports():
try: try:
exports = callGAPIpages(v.matters().exports(), 'list', 'exports', exports = callGAPIpages(v.matters().exports(), 'list', 'exports',
pageMessage=pageMessage, pageMessage=pageMessage,
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields=fields) matterId=matterId, fields=fields)
except GAPI.failedPrecondition: except GAPI.failedPrecondition:
warnMatterNotOpen(v, matter, matterNameId, j, jcount) warnMatterNotOpen(v, matter, matterNameId, j, jcount)
continue continue
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_EXPORT, None], str(e)) entityActionFailedWarning([Ent.VAULT_EXPORT, None], str(e))
break break
else: else:
@@ -44360,7 +44362,7 @@ def doCopyVaultExport():
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION], GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
matterId=matterId, exportId=exportId) matterId=matterId, exportId=exportId)
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
return return
if export['status'] == 'COMPLETED': if export['status'] == 'COMPLETED':
@@ -44461,7 +44463,7 @@ def doDownloadVaultExport():
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION], GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
matterId=matterId, exportId=exportId) matterId=matterId, exportId=exportId)
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
return return
if export['status'] == 'COMPLETED': if export['status'] == 'COMPLETED':
@@ -44687,7 +44689,7 @@ def doCreateVaultHold():
try: try:
hold = callGAPI(v.matters().holds(), 'create', hold = callGAPI(v.matters().holds(), 'create',
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.BAD_REQUEST, GAPI.BACKEND_ERROR, GAPI.FAILED_PRECONDITION, throwReasons=[GAPI.ALREADY_EXISTS, GAPI.BAD_REQUEST, GAPI.BACKEND_ERROR, GAPI.FAILED_PRECONDITION,
GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, body=body) matterId=matterId, body=body)
if not returnIdOnly: if not returnIdOnly:
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, formatVaultNameId(hold['name'], hold['holdId'])]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, formatVaultNameId(hold['name'], hold['holdId'])])
@@ -44696,7 +44698,7 @@ def doCreateVaultHold():
else: else:
writeStdout(f'{hold["holdId"]}\n') writeStdout(f'{hold["holdId"]}\n')
except (GAPI.alreadyExists, GAPI.badRequest, GAPI.backendError, GAPI.failedPrecondition, except (GAPI.alreadyExists, GAPI.badRequest, GAPI.backendError, GAPI.failedPrecondition,
GAPI.forbidden, GAPI.invalidArgument) as e: GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, body.get('name')], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, body.get('name')], str(e))
# gam update vaulthold|hold <HoldItem> matter <MatterItem> # gam update vaulthold|hold <HoldItem> matter <MatterItem>
@@ -44741,9 +44743,9 @@ def doUpdateVaultHold():
missingArgumentExit('matter') missingArgumentExit('matter')
try: try:
old_body = callGAPI(v.matters().holds(), 'get', old_body = callGAPI(v.matters().holds(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId, fields='name,corpus,query,orgUnit') matterId=matterId, holdId=holdId, fields='name,corpus,query,orgUnit')
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e))
return return
accountType = 'group' if old_body['corpus'] == 'GROUPS' else 'user' accountType = 'group' if old_body['corpus'] == 'GROUPS' else 'user'
@@ -44769,10 +44771,10 @@ def doUpdateVaultHold():
if body: if body:
try: try:
hold = callGAPI(v.matters().holds(), 'update', hold = callGAPI(v.matters().holds(), 'update',
throwReas=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReas=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId, body=body) matterId=matterId, holdId=holdId, body=body)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId])
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e))
return return
jcount = len(addAccountIds) jcount = len(addAccountIds)
@@ -44785,12 +44787,12 @@ def doUpdateVaultHold():
j += 1 j += 1
try: try:
callGAPI(v.matters().holds().accounts(), 'create', callGAPI(v.matters().holds().accounts(), 'create',
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.BACKEND_ERROR, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.ALREADY_EXISTS, GAPI.BACKEND_ERROR, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId, body={'accountId': account['id']}) matterId=matterId, holdId=holdId, body={'accountId': account['id']})
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], j, jcount) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], j, jcount)
except (GAPI.alreadyExists, GAPI.backendError) as e: except (GAPI.alreadyExists, GAPI.backendError) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], str(e), j, jcount) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], str(e), j, jcount)
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, None], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, None], str(e))
return return
Ind.Decrement() Ind.Decrement()
@@ -44806,12 +44808,12 @@ def doUpdateVaultHold():
j += 1 j += 1
try: try:
callGAPI(v.matters().holds().accounts(), 'delete', callGAPI(v.matters().holds().accounts(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.BACKEND_ERROR, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BACKEND_ERROR, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId, accountId=account['id']) matterId=matterId, holdId=holdId, accountId=account['id'])
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], j, jcount) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], j, jcount)
except (GAPI.alreadyExists, GAPI.backendError) as e: except (GAPI.alreadyExists, GAPI.backendError) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], str(e), j, jcount) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId, Ent.ACCOUNT, account['email']], str(e), j, jcount)
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, None], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, None], str(e))
return return
Ind.Decrement() Ind.Decrement()
@@ -44836,10 +44838,10 @@ def doDeleteVaultHold():
unknownArgumentExit() unknownArgumentExit()
try: try:
callGAPI(v.matters().holds(), 'delete', callGAPI(v.matters().holds(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId) matterId=matterId, holdId=holdId)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId])
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e))
VAULT_HOLD_FIELDS_CHOICE_MAP = { VAULT_HOLD_FIELDS_CHOICE_MAP = {
@@ -44889,10 +44891,10 @@ def doInfoVaultHold():
fields = getFieldsFromFieldsList(fieldsList) fields = getFieldsFromFieldsList(fieldsList)
try: try:
hold = callGAPI(v.matters().holds(), 'get', hold = callGAPI(v.matters().holds(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, holdId=holdId, fields=fields) matterId=matterId, holdId=holdId, fields=fields)
_showVaultHold(matterNameId, hold, cd, FJQC) _showVaultHold(matterNameId, hold, cd, FJQC)
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_HOLD, holdNameId], str(e))
PRINT_VAULT_HOLDS_TITLES = ['matterId', 'matterName', 'holdId', 'name', 'updateTime'] PRINT_VAULT_HOLDS_TITLES = ['matterId', 'matterName', 'holdId', 'name', 'updateTime']
@@ -44943,9 +44945,9 @@ def doPrintShowVaultHolds():
try: try:
results = callGAPIpages(v.matters(), 'list', 'matters', results = callGAPIpages(v.matters(), 'list', 'matters',
pageMessage=getPageMessage(), pageMessage=getPageMessage(),
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken') view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e)) entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e))
return return
else: else:
@@ -44972,12 +44974,12 @@ def doPrintShowVaultHolds():
try: try:
holds = callGAPIpages(v.matters().holds(), 'list', 'holds', holds = callGAPIpages(v.matters().holds(), 'list', 'holds',
pageMessage=pageMessage, pageMessage=pageMessage,
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields=fields) matterId=matterId, fields=fields)
except GAPI.failedPrecondition: except GAPI.failedPrecondition:
warnMatterNotOpen(v, matter, matterNameId, j, jcount) warnMatterNotOpen(v, matter, matterNameId, j, jcount)
continue continue
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e)) entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e))
break break
else: else:
@@ -45023,9 +45025,9 @@ def printShowUserVaultHolds(entityList):
try: try:
matters = callGAPIpages(v.matters(), 'list', 'matters', matters = callGAPIpages(v.matters(), 'list', 'matters',
pageMessage=getPageMessage(), pageMessage=getPageMessage(),
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken') view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e)) entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e))
return return
jcount = len(matters) jcount = len(matters)
@@ -45039,11 +45041,11 @@ def printShowUserVaultHolds(entityList):
try: try:
matter['holds'] = callGAPIpages(v.matters().holds(), 'list', 'holds', matter['holds'] = callGAPIpages(v.matters().holds(), 'list', 'holds',
pageMessage=getPageMessageForWhom(), pageMessage=getPageMessageForWhom(),
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields='holds(holdId,name,accounts(accountId,email),orgUnit(orgUnitId)),nextPageToken') matterId=matterId, fields='holds(holdId,name,accounts(accountId,email),orgUnit(orgUnitId)),nextPageToken')
except GAPI.failedPrecondition: except GAPI.failedPrecondition:
warnMatterNotOpen(v, matter, matterNameId, j, jcount) warnMatterNotOpen(v, matter, matterNameId, j, jcount)
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e), j, jcount) entityActionFailedWarning([Ent.VAULT_HOLD, None], str(e), j, jcount)
totalHolds = 0 totalHolds = 0
_, _, entityList = getEntityArgument(entityList) _, _, entityList = getEntityArgument(entityList)
@@ -45157,7 +45159,7 @@ def doCreateCopyVaultQuery(copyCmd):
resultNameId = matterNameId resultNameId = matterNameId
try: try:
result = callGAPI(v.matters().savedQueries(), 'create', result = callGAPI(v.matters().savedQueries(), 'create',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT, GAPI.ALREADY_EXISTS], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT, GAPI.ALREADY_EXISTS],
matterId=resultId, body=body) matterId=resultId, body=body)
if not returnIdOnly: if not returnIdOnly:
if not FJQC.formatJSON: if not FJQC.formatJSON:
@@ -45166,7 +45168,7 @@ def doCreateCopyVaultQuery(copyCmd):
_showVaultQuery(resultNameId, result, cd, drive, FJQC) _showVaultQuery(resultNameId, result, cd, drive, FJQC)
else: else:
writeStdout(f'{result["savedQueryId"]}\n') writeStdout(f'{result["savedQueryId"]}\n')
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.alreadyExists) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.alreadyExists) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, resultNameId, Ent.VAULT_QUERY, body['displayName']], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, resultNameId, Ent.VAULT_QUERY, body['displayName']], str(e))
# gam create vaultquery <MatterItem> [name <String>] # gam create vaultquery <MatterItem> [name <String>]
@@ -45214,10 +45216,10 @@ def doDeleteVaultQuery():
unknownArgumentExit() unknownArgumentExit()
try: try:
callGAPI(v.matters().savedQueries(), 'delete', callGAPI(v.matters().savedQueries(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, savedQueryId=queryId) matterId=matterId, savedQueryId=queryId)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId])
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e))
VAULT_QUERY_FIELDS_CHOICE_MAP = { VAULT_QUERY_FIELDS_CHOICE_MAP = {
@@ -45263,10 +45265,10 @@ def doInfoVaultQuery():
fields = getFieldsFromFieldsList(fieldsList) fields = getFieldsFromFieldsList(fieldsList)
try: try:
query = callGAPI(v.matters().savedQueries(), 'get', query = callGAPI(v.matters().savedQueries(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, savedQueryId=queryId, fields=fields) matterId=matterId, savedQueryId=queryId, fields=fields)
_showVaultQuery(matterNameId, query, cd, drive, FJQC) _showVaultQuery(matterNameId, query, cd, drive, FJQC)
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e))
PRINT_VAULT_QUERIES_TITLES = ['matterId', 'matterName', 'savedQueryId', 'displayName'] PRINT_VAULT_QUERIES_TITLES = ['matterId', 'matterName', 'savedQueryId', 'displayName']
@@ -45307,9 +45309,9 @@ def doPrintShowVaultQueries():
try: try:
results = callGAPIpages(v.matters(), 'list', 'matters', results = callGAPIpages(v.matters(), 'list', 'matters',
pageMessage=getPageMessage(), pageMessage=getPageMessage(),
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken') view='BASIC', state='OPEN', fields='matters(matterId,name,state),nextPageToken')
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_QUERY, None], str(e)) entityActionFailedWarning([Ent.VAULT_QUERY, None], str(e))
return return
else: else:
@@ -45336,12 +45338,12 @@ def doPrintShowVaultQueries():
try: try:
queries = callGAPIpages(v.matters().savedQueries(), 'list', 'savedQueries', queries = callGAPIpages(v.matters().savedQueries(), 'list', 'savedQueries',
pageMessage=pageMessage, pageMessage=pageMessage,
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, fields=fields) matterId=matterId, fields=fields)
except GAPI.failedPrecondition: except GAPI.failedPrecondition:
warnMatterNotOpen(v, matter, matterNameId, j, jcount) warnMatterNotOpen(v, matter, matterNameId, j, jcount)
continue continue
except (GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_QUERY, None], str(e)) entityActionFailedWarning([Ent.VAULT_QUERY, None], str(e))
break break
else: else:
@@ -45428,7 +45430,7 @@ def doCreateVaultMatter():
body['name'] = f'GAM Matter - {ISOformatTimeStamp(todaysTime())}' body['name'] = f'GAM Matter - {ISOformatTimeStamp(todaysTime())}'
try: try:
matter = callGAPI(v.matters(), 'create', matter = callGAPI(v.matters(), 'create',
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.ALREADY_EXISTS, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
body=body) body=body)
matterId = matter['matterId'] matterId = matter['matterId']
matterNameId = formatVaultNameId(matter['name'], matterId) matterNameId = formatVaultNameId(matter['name'], matterId)
@@ -45436,7 +45438,7 @@ def doCreateVaultMatter():
entityActionPerformed([Ent.VAULT_MATTER, matterNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId])
else: else:
writeStdout(f'{matterId}\n') writeStdout(f'{matterId}\n')
except (GAPI.alreadyExists, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.alreadyExists, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, body['name']], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, body['name']], str(e))
return return
jcount = len(collaborators) jcount = len(collaborators)
@@ -45451,11 +45453,11 @@ def doCreateVaultMatter():
cbody['matterPermission']['accountId'] = collaborator['id'] cbody['matterPermission']['accountId'] = collaborator['id']
try: try:
callGAPI(v.matters(), 'addPermissions', callGAPI(v.matters(), 'addPermissions',
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, body=cbody) matterId=matterId, body=cbody)
if not returnIdOnly: if not returnIdOnly:
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount)
except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
break break
Ind.Decrement() Ind.Decrement()
@@ -45479,10 +45481,10 @@ def doActionVaultMatter(action, matterId=None, matterNameId=None, v=None):
action_kwargs = {} if action == 'delete' else {'body': {}} action_kwargs = {} if action == 'delete' else {'body': {}}
try: try:
callGAPI(v.matters(), action, callGAPI(v.matters(), action,
throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, **action_kwargs) matterId=matterId, **action_kwargs)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId])
except (GAPI.notFound, GAPI.failedPrecondition, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.failedPrecondition, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
# gam close vaultmatter|matter <MatterItem> # gam close vaultmatter|matter <MatterItem>
@@ -45535,7 +45537,7 @@ def doUpdateVaultMatter():
if 'name' not in body or 'description' not in body: if 'name' not in body or 'description' not in body:
# bah, API requires name/description to be sent on update even when it's not changing # bah, API requires name/description to be sent on update even when it's not changing
result = callGAPI(v.matters(), 'get', result = callGAPI(v.matters(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, view='BASIC') matterId=matterId, view='BASIC')
body.setdefault('name', result['name']) body.setdefault('name', result['name'])
body.setdefault('description', result.get('description')) body.setdefault('description', result.get('description'))
@@ -45543,7 +45545,7 @@ def doUpdateVaultMatter():
throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN], throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN],
matterId=matterId, body=body) matterId=matterId, body=body)
entityActionPerformed([Ent.VAULT_MATTER, matterNameId]) entityActionPerformed([Ent.VAULT_MATTER, matterNameId])
except (GAPI.notFound, GAPI.failedPrecondition, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.failedPrecondition, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
return return
jcount = len(addCollaborators) jcount = len(addCollaborators)
@@ -45556,10 +45558,10 @@ def doUpdateVaultMatter():
j += 1 j += 1
try: try:
callGAPI(v.matters(), 'addPermissions', callGAPI(v.matters(), 'addPermissions',
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, body={'matterPermission': {'role': 'COLLABORATOR', 'accountId': collaborator['id']}}) matterId=matterId, body={'matterPermission': {'role': 'COLLABORATOR', 'accountId': collaborator['id']}})
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount)
except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
break break
Ind.Decrement() Ind.Decrement()
@@ -45573,10 +45575,10 @@ def doUpdateVaultMatter():
j += 1 j += 1
try: try:
callGAPI(v.matters(), 'removePermissions', callGAPI(v.matters(), 'removePermissions',
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, body={'accountId': collaborator['id']}) matterId=matterId, body={'accountId': collaborator['id']})
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount) entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.COLLABORATOR, collaborator['email']], j, jcount)
except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.failedPrecondition, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
break break
Ind.Decrement() Ind.Decrement()
@@ -45609,11 +45611,11 @@ def doInfoVaultMatter():
fields = getFieldsFromFieldsList(fieldsList) fields = getFieldsFromFieldsList(fieldsList)
try: try:
matter = callGAPI(v.matters(), 'get', matter = callGAPI(v.matters(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT],
matterId=matterId, view=view, fields=fields) matterId=matterId, view=view, fields=fields)
cd = buildGAPIObject(API.DIRECTORY) if 'matterPermissions' in matter else None cd = buildGAPIObject(API.DIRECTORY) if 'matterPermissions' in matter else None
_showVaultMatter(matter, cd, FJQC) _showVaultMatter(matter, cd, FJQC)
except (GAPI.notFound, GAPI.forbidden, GAPI.invalidArgument) as e: except (GAPI.notFound, GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId], str(e))
VAULT_MATTER_STATE_MAP = {'open': 'OPEN', 'closed': 'CLOSED', 'deleted': 'DELETED'} VAULT_MATTER_STATE_MAP = {'open': 'OPEN', 'closed': 'CLOSED', 'deleted': 'DELETED'}
@@ -45669,9 +45671,9 @@ def doPrintShowVaultMatters():
try: try:
matters = callGAPIpages(v.matters(), 'list', 'matters', matters = callGAPIpages(v.matters(), 'list', 'matters',
pageMessage=getPageMessage(), pageMessage=getPageMessage(),
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT, GAPI.INVALID_ARGUMENT], throwReasons=[GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.INVALID_ARGUMENT, GAPI.INVALID_ARGUMENT],
view=view, state=stateParm, fields=fields) view=view, state=stateParm, fields=fields)
except (GAPI.forbidden, GAPI.invalidArgument, GAPI.invalidArgument) as e: except (GAPI.forbidden, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.VAULT_MATTER, None], str(e)) entityActionFailedWarning([Ent.VAULT_MATTER, None], str(e))
return return
jcount = len(matters) jcount = len(matters)
@@ -55348,16 +55350,30 @@ def printShowCalendarACLs(users):
if csvPF: if csvPF:
csvPF.writeCSVfile('Calendar ACLs') csvPF.writeCSVfile('Calendar ACLs')
# gam <UserTypeEntity> transfer calendars|seccals <UserItem> [<UserCalendarEntity>] # gam <CalendarEntity> transfer ownership <UserItem>
# [keepuser | (retainrole <CalendarACLRole>)] [sendnotifications <Boolean>] [noretentionmessages] def doCalendarsTransferOwnership(calIds):
# <CalendarSettings>* [append description|location|summary] [noupdatemessages] Act.Set(Act.TRANSFER_OWNERSHIP)
# [deletefromoldowner] [addtonewowner <CalendarAttribute>*] [nolistmessages] newDataOwner = getEmailAddress()
def transferCalendars(users): checkForExtraneousArguments()
errMsg = ''' Due to the following Calendar API update, the `gam <UserTypeEntity> transfer calendars` command has been removed. count = len(calIds)
* See: https://developers.google.com/workspace/calendar/release-notes#October_27_2025 i = 0
Data ownership can be transferred in the Google Calendar UI. for calId in calIds:
''' i += 1
systemErrorExit(ACTION_FAILED_RC, errMsg) calId, cal = validateCalendar(calId, i, count)
if not cal:
continue
try:
callGAPI(cal.calendars(), 'transferOwnership',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_PARAMETER,
GAPI.FORBIDDEN, GAPI.AUTH_ERROR, GAPI.CONDITION_NOT_MET],
calendarId=calId, newDataOwner=newDataOwner, useAdminAccess=True)
entityPerformActionModifierNewValue([Ent.CALENDAR, calId], Act.MODIFIER_TO, newDataOwner, i, count)
except (GAPI.notFound, GAPI.invalid, GAPI.invalidParameter,
GAPI.forbidden, GAPI.authError, GAPI.conditionNotMet) as e:
entityModifierNewValueActionFailedWarning([Ent.CALENDAR, calId], Act.MODIFIER_TO, newDataOwner, str(e), i, count)
except AttributeError as e:
entityModifierNewValueActionFailedWarning([Ent.CALENDAR, calId], Act.MODIFIER_TO, newDataOwner, str(e), i, count)
return
def _createImportCalendarEvent(users, function): def _createImportCalendarEvent(users, function):
calendarEntity = getUserCalendarEntity() calendarEntity = getUserCalendarEntity()
@@ -81570,6 +81586,11 @@ CALENDARS_SUBCOMMANDS_WITH_OBJECTS = {
Cmd.ARG_SETTINGS: doCalendarsPrintShowSettings, Cmd.ARG_SETTINGS: doCalendarsPrintShowSettings,
} }
), ),
'transfer':
(Act.TRANSFER,
{Cmd.ARG_OWNERSHIP: doCalendarsTransferOwnership,
}
),
'update': 'update':
(Act.UPDATE, (Act.UPDATE,
{Cmd.ARG_CALENDARACL: doCalendarsUpdateACLs, {Cmd.ARG_CALENDARACL: doCalendarsUpdateACLs,
@@ -82304,7 +82325,6 @@ USER_COMMANDS_WITH_OBJECTS = {
'transfer': 'transfer':
(Act.TRANSFER, (Act.TRANSFER,
{Cmd.ARG_DRIVE: transferDrive, {Cmd.ARG_DRIVE: transferDrive,
Cmd.ARG_CALENDAR: transferCalendars,
Cmd.ARG_OWNERSHIP: transferOwnership, Cmd.ARG_OWNERSHIP: transferOwnership,
} }
), ),

View File

@@ -10,6 +10,11 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
### 7.46.02
Updated `gam calendars <CalendarEntity> show settings` to display `dataOwner` field;
it is labelled `Owner`.
### 7.46.01 ### 7.46.01
Fixed bug in `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> ... csv` where Fixed bug in `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> ... csv` where

View File

@@ -251,7 +251,7 @@ writes the credentials into the file oauth2.txt.
``` ```
gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt
gamteam@server:/Users/gamteam$ gam version gamteam@server:/Users/gamteam$ gam version
GAM 7.46.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.46.02 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
macOS Tahoe 26.5.1 arm64 macOS Tahoe 26.5.1 arm64
@@ -1034,7 +1034,7 @@ writes the credentials into the file oauth2.txt.
``` ```
C:\>del C:\GAMConfig\oauth2.txt C:\>del C:\GAMConfig\oauth2.txt
C:\>gam version C:\>gam version
GAM 7.46.01 - https://github.com/GAM-team/GAM - pythonsource GAM 7.46.02 - https://github.com/GAM-team/GAM - pythonsource
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
Windows 11 10.0.26200 AMD64 Windows 11 10.0.26200 AMD64

View File

@@ -535,6 +535,14 @@ User: user@domain.com, Delete maximum of 15 Other Contacts
User: user@domain.com, Other Contact: otherContacts/c6318452176100245073, Deleted User: user@domain.com, Other Contact: otherContacts/c6318452176100245073, Deleted
``` ```
Bulk delete Other Contacts
Let's suppose you have a CSV file (OtherContacts.csv) with at least these two headers: User,resourceName
The file can contain multiple users and their other contacts. This is the most API effecient way to delete the contacts.
```
gam redirect stdout ./DeleteOtherContacts.txt redirect stderr stdout csvkmd users OtherContacts.csv keyfield User datafield resourceName delete othercontacts csvdata resourceName
```
## Display User Other Contacts ## Display User Other Contacts
### Display as an indented list of keys and values. ### Display as an indented list of keys and values.
``` ```

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details Print the current version of Gam with details
``` ```
gam version gam version
GAM 7.46.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.46.02 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
macOS Tahoe 26.5.1 arm64 macOS Tahoe 26.5.1 arm64
@@ -15,7 +15,7 @@ Time: 2026-02-15T07:51:00-08:00
Print the current version of Gam with details and time offset information Print the current version of Gam with details and time offset information
``` ```
gam version timeoffset gam version timeoffset
GAM 7.46.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.46.02 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
macOS Tahoe 26.5.1 arm64 macOS Tahoe 26.5.1 arm64
@@ -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 Print the current version of Gam with extended details and SSL information
``` ```
gam version extended gam version extended
GAM 7.46.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.46.02 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
macOS Tahoe 26.5.1 arm64 macOS Tahoe 26.5.1 arm64
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/gamteam/bin/gam7 Path: /Users/gamteam/bin/gam7
Version Check: Version Check:
Current: 5.35.08 Current: 5.35.08
Latest: 7.46.01 Latest: 7.46.02
echo $? echo $?
1 1
``` ```
@@ -76,7 +76,7 @@ echo $?
Print the current version number without details Print the current version number without details
``` ```
gam version simple gam version simple
7.46.01 7.46.02
``` ```
In Linux/MacOS you can do: In Linux/MacOS you can do:
``` ```
@@ -86,7 +86,7 @@ echo $VER
Print the current version of Gam and address of this Wiki Print the current version of Gam and address of this Wiki
``` ```
gam help gam help
GAM 7.46.01 - https://github.com/GAM-team/GAM GAM 7.46.02 - https://github.com/GAM-team/GAM
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.6 64-bit final Python 3.14.6 64-bit final
macOS Tahoe 26.5.1 arm64 macOS Tahoe 26.5.1 arm64