Skip to content

Action move user org unit#65

Open
cvkramer wants to merge 4 commits into
mainfrom
action_move_user_org_unit
Open

Action move user org unit#65
cvkramer wants to merge 4 commits into
mainfrom
action_move_user_org_unit

Conversation

@cvkramer
Copy link
Copy Markdown
Contributor

@cvkramer cvkramer commented Jan 21, 2026

Summary

Adds a new action move_account_to_org_unit that allows moving Google Workspace accounts to different organizational units.

Changes

New Action: move_account_to_org_unit

Implements a new connector action to move accounts between organizational units in Google Workspace.

Parameters:

  • user_id (required): ID of the user resource to move
  • org_unit_path (required): Full path of the organizational unit (e.g., /Sales or /Engineering/Backend). Use / for root.

Returns:

  • success: Whether the account was moved successfully
  • previous_org_unit_path: Account's previous organizational unit path
  • new_org_unit_path: Account's new organizational unit path

Key Features:

  • Idempotent: Checks if account is already at target org unit before updating
  • Validation: Ensures org unit path starts with / and parameters are non-empty
  • Root handling: Treats empty string and / as equivalent root paths
  • Comprehensive testing: 6 test cases covering success, idempotency, validation, and edge cases

API Scope Required:

  • https://www.googleapis.com/auth/admin.directory.user

Implementation Details:

  • Uses Google Admin SDK Directory API
  • Action type: ACTION_TYPE_ACCOUNT
  • Follows same pattern as existing disable_user/enable_user actions
  • Uses ForceSendFields to ensure OrgUnitPath is sent even when empty

Testing

go test ./pkg/connector/... -v -run TestMoveAccountToOrgUnit

All tests pass:
- TestMoveAccountToOrgUnit_Success
- TestMoveAccountToOrgUnit_Idempotent
- TestMoveAccountToOrgUnit_RootOrgUnit
- TestMoveAccountToOrgUnit_EmptyOrgUnitPathTreatedAsRoot
- TestMoveAccountToOrgUnit_ValidationErrors
- TestMoveAccountToOrgUnit_UserNotFound

Usage Example

baton-google-workspace \
  --credentials-json-file-path "credentials.json" \
  --administrator-email "admin@example.com" \
  --customer-id "C01234567" \
  --invoke-action=move_account_to_org_unit \
  --invoke-action-args='{"user_id":"user@example.com", "org_unit_path":"/Engineering"}'

You can adjust or simplify this as needed. The key points are all covered: what the action does, the parameters, idempotency, validation, and testing.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
* Added action to move user accounts to different Google Workspace organizational units.
* Includes idempotency detection to prevent unnecessary API calls when accounts are already at the target location.
* Returns previous and new organizational unit paths for change tracking.

* **Tests**
* Comprehensive test coverage added for account movement functionality, including edge cases, validation scenarios, and error handling.

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@cvkramer cvkramer requested a review from a team January 21, 2026 19:19
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 21, 2026

Walkthrough

A new idempotent action moveAccountToOrgUnit is added to the GoogleWorkspace connector. It validates user inputs, fetches the current organizational unit path for idempotency checking, and updates the user's organizational unit via the Directory API, returning previous and new paths.

Changes

Cohort / File(s) Summary
Core Action Implementation
pkg/connector/actions.go
Introduces moveAccountToOrgUnit handler function with input validation (non-empty user_id and org_unit_path with leading slash), idempotency checking via Directory API fetch, conditional update logic with ForceSendFields, and structured response containing previous/new organizational unit paths. Includes error handling for missing parameters and API failures.
Test Coverage
pkg/connector/actions_test.go
Extends testUser struct with OrgUnitPath field; updates mock Directory API responses to serialize/deserialize OrgUnitPath; adds 231 lines of test cases covering success paths, idempotent behavior, root path handling, empty path semantics, input validation (missing/empty fields, format checks), and user-not-found scenarios.
Action Registration
pkg/connector/connector.go
Defines moveAccountToOrgUnitActionSchema with required inputs (user_id, org_unit_path) and output fields (success, previous_org_unit_path, new_org_unit_path); registers the action in GlobalActions onboarding flow, wiring to the handler implementation.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Connector as GoogleWorkspace<br/>Connector
    participant API as Directory API

    Caller->>Connector: moveAccountToOrgUnit(user_id, org_unit_path)
    Connector->>Connector: Validate inputs (non-empty, leading slash)
    Connector->>API: GET user details (current OrgUnitPath)
    API-->>Connector: User data with current OrgUnitPath
    Connector->>Connector: Check idempotency<br/>(current == target?)
    alt Paths Match (Idempotent)
        Connector-->>Caller: Success (no API update needed)
    else Paths Differ
        Connector->>API: PUT user with new OrgUnitPath
        API-->>Connector: Updated user data
        Connector-->>Caller: Success with previous & new paths
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A hop through org units we go,
Moving users high and low,
With idempotent care so true,
No double-moves will we do!
The Directory API's our friend,
On this migration quest to blend. 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely summarizes the main change: adding a new action to move user accounts to organization units in Google Workspace.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/connector/actions.go`:
- Around line 405-419: The validation currently rejects an empty org_unit_path
but PR intent is to treat empty (after trimming) as the root path; in the
orgUnitPath handling in actions.go (variables userId, orgUnitPath from
userIdField.StringValue and orgUnitPathField.StringValue) change the logic so
that after trimming whitespace you: 1) keep rejecting whitespace-only inputs, 2)
if orgUnitPath == "" set orgUnitPath = "/" instead of returning an error, and 3)
retain the existing HasPrefix check to ensure non-root values start with "/"
(e.g., ensure "/" is accepted and paths like "/Sales" continue to be validated).
Ensure tests updated accordingly.

Comment thread pkg/connector/actions.go
Comment on lines +405 to +419
userId := strings.TrimSpace(userIdField.StringValue)
orgUnitPath := strings.TrimSpace(orgUnitPathField.StringValue)

// Validate non-empty
if userId == "" {
return nil, nil, fmt.Errorf("user_id must be non-empty")
}
if orgUnitPath == "" {
return nil, nil, fmt.Errorf("org_unit_path must be non-empty")
}

// Ensure org_unit_path starts with "/"
if !strings.HasPrefix(orgUnitPath, "/") {
return nil, nil, fmt.Errorf("org_unit_path must start with '/' (e.g., '/Sales' or '/Engineering/Backend')")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Honor “empty org_unit_path = root” if that’s a supported input.

PR objectives state empty string should map to root, but the current validation rejects it. If that behavior is intended, map empty to “/” (while keeping whitespace-only invalid) and update the corresponding tests.

🛠️ Proposed fix
-	orgUnitPath := strings.TrimSpace(orgUnitPathField.StringValue)
+	rawOrgUnitPath := orgUnitPathField.StringValue
+	orgUnitPath := strings.TrimSpace(rawOrgUnitPath)
 
 	// Validate non-empty
 	if userId == "" {
 		return nil, nil, fmt.Errorf("user_id must be non-empty")
 	}
-	if orgUnitPath == "" {
-		return nil, nil, fmt.Errorf("org_unit_path must be non-empty")
-	}
+	if rawOrgUnitPath == "" {
+		orgUnitPath = "/"
+	} else if orgUnitPath == "" {
+		return nil, nil, fmt.Errorf("org_unit_path must be non-empty")
+	}
🤖 Prompt for AI Agents
In `@pkg/connector/actions.go` around lines 405 - 419, The validation currently
rejects an empty org_unit_path but PR intent is to treat empty (after trimming)
as the root path; in the orgUnitPath handling in actions.go (variables userId,
orgUnitPath from userIdField.StringValue and orgUnitPathField.StringValue)
change the logic so that after trimming whitespace you: 1) keep rejecting
whitespace-only inputs, 2) if orgUnitPath == "" set orgUnitPath = "/" instead of
returning an error, and 3) retain the existing HasPrefix check to ensure
non-root values start with "/" (e.g., ensure "/" is accepted and paths like
"/Sales" continue to be validated). Ensure tests updated accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants