Class DropboxApiCommands

java.lang.Object
edu.internet2.middleware.grouper.app.dropbox.DropboxApiCommands

public class DropboxApiCommands extends Object
Low-level HTTP command layer for the Dropbox Business Team API.

This class mirrors TrueFoundryApiCommands: every method opens a fresh GrouperHttpClient, attaches bearer authentication, POSTs a JSON body, tracks per-command timing via GrouperProvisioner.incrementCommandsCallsStats(String, int, long), and parses the JSON response with the GrouperUtil.jsonJackson* helpers. The Dropbox DAO and the provisioner tests call the public static methods here.

Auth difference from TrueFoundry. TrueFoundry stores a JSON blob of two tokens in the accessTokenPassword; Dropbox uses a single, plain bearer token, so attachAuthentication(GrouperHttpClient, String) reads accessTokenPassword as the literal token string with no JSON parse. The base URL comes from grouper.wsBearerToken.<configId>.endpoint (the mock service path in tests, https://api.dropboxapi.com in prod) -- never hardcoded.

Dropbox conventions. Every Team API call is a POST with a JSON object body (use "{}" when there are no arguments). Unions are encoded with a ".tag" discriminator. Long-running write operations (group delete, group member add/remove, member add/remove) may return an async_job_id instead of completing inline; this layer polls the matching .../job_status/... endpoint until the job reports {".tag":"complete"} (see pollIfAsync(java.util.Map<java.lang.String, java.lang.Object>, java.lang.String, com.fasterxml.jackson.databind.JsonNode, java.lang.String)). The mock returns synchronous complete results, so polling is a no-op under test but is required in production.

  • Field Details

    • doNotLogHeaders

      public static final Set<String> doNotLogHeaders
      the bearer token header carries secrets, so never log it
    • grouperLoaderConfig

      public static GrouperLoaderConfig grouperLoaderConfig
      loader config, used to resolve endpoint + token by configId
  • Constructor Details

    • DropboxApiCommands

      public DropboxApiCommands()
  • Method Details

    • retrieveAdminRoleNameToId

      public static Map<String,String> retrieveAdminRoleNameToId(String configId)
      Return the cached admin-role name -> role_id map for a config. Dropbox has no standalone roles-list endpoint, so the catalog is populated as a side effect of retrieveDropboxUsers(String) (which harvests each member's roles[]). If the cache is empty, a member read is triggered once to populate it.
      Parameters:
      configId - external system config id
      Returns:
      a copy of the admin-role name -> role_id map (possibly empty, never null)
    • retrieveAdminRoleNameToId

      public static Map<String,String> retrieveAdminRoleNameToId(String configId, String teamMemberId)
      Return the admin-role name -> role_id catalog, fetching it authoritatively from POST /2/team/members/list_member_roles for the given member. Dropbox's list_member_roles returns the full set of assignable admin roles for the team (not just the member's current roles), so this works even on a fresh team where no member yet has an admin role -- unlike harvesting roles[] from member reads. Results are merged into the per-config cache.
      Parameters:
      configId - external system config id
      teamMemberId - a native team_member_id to scope the list_member_roles call
      Returns:
      a copy of the admin-role name -> role_id map (possibly empty, never null)
    • retrieveDropboxGroups

      public static List<DropboxGroup> retrieveDropboxGroups(String configId)
      Retrieve all Dropbox team groups via POST /2/team/groups/list, paging with /2/team/groups/list/continue until has_more is false.
      Parameters:
      configId - external system config id
      Returns:
      the list of DropboxGroup objects
    • retrieveDropboxGroup

      public static DropboxGroup retrieveDropboxGroup(String configId, String groupId)
      Retrieve a single Dropbox group by its native group_id via POST /2/team/groups/get_info. The response is an array of GroupsGetInfoItem; a not-found id yields an id_not_found entry, for which null is returned.
      Parameters:
      configId - external system config id
      groupId - the native Dropbox group_id (e.g. "g:abc123")
      Returns:
      the DropboxGroup, or null if not found
    • createDropboxGroup

      public static DropboxGroup createDropboxGroup(String configId, DropboxGroup dropboxGroup)
      Create a Dropbox team group via POST /2/team/groups/create. The response is a GroupFullInfo carrying the assigned native group_id.
      Parameters:
      configId - external system config id
      dropboxGroup - the group to create (must have name set)
      Returns:
      the created DropboxGroup with id populated
    • updateDropboxGroup

      public static DropboxGroup updateDropboxGroup(String configId, DropboxGroup dropboxGroup)
      Update a Dropbox team group via POST /2/team/groups/update. The group is selected by group_id; only the supplied new_* fields are changed. The response is a GroupFullInfo.
      Parameters:
      configId - external system config id
      dropboxGroup - the group to update (must have id set)
      Returns:
      the updated DropboxGroup
    • deleteDropboxGroup

      public static void deleteDropboxGroup(String configId, String groupId)
      Delete a Dropbox team group via POST /2/team/groups/delete. The response is a LaunchEmptyResult, which may be asynchronous; this method polls /2/team/groups/job_status/get until the delete completes.
      Parameters:
      configId - external system config id
      groupId - the native Dropbox group_id to delete
    • retrieveDropboxGroupMemberships

      public static List<DropboxMembership> retrieveDropboxGroupMemberships(String configId, String groupId)
      Retrieve all memberships of a Dropbox group via POST /2/team/groups/members/list, paging with /2/team/groups/members/list/continue until has_more is false. Each GroupMemberInfo is converted into a DropboxMembership (group_id + team_member_id + access_type).
      Parameters:
      configId - external system config id
      groupId - the native Dropbox group_id
      Returns:
      the list of memberships
    • addDropboxGroupMembers

      public static void addDropboxGroupMembers(String configId, String groupId, List<DropboxMembership> memberships)
      Add members to a Dropbox group via POST /2/team/groups/members/add. Each membership becomes a {"user":UserSelectorArg,"access_type":{".tag":..}} entry. The response is a GroupMembersChangeResult which may carry an async_job_id; this method polls /2/team/groups/job_status/get to completion.
      Parameters:
      configId - external system config id
      groupId - the native Dropbox group_id
      memberships - the memberships to add (team_member_id + access_type)
    • removeDropboxGroupMembers

      public static void removeDropboxGroupMembers(String configId, String groupId, List<String> teamMemberIds)
      Remove members from a Dropbox group via POST /2/team/groups/members/remove. The response is a GroupMembersChangeResult which may carry an async_job_id; this method polls /2/team/groups/job_status/get to completion.
      Parameters:
      configId - external system config id
      groupId - the native Dropbox group_id
      teamMemberIds - the native team_member_ids to remove
    • retrieveDropboxUsers

      public static List<DropboxUser> retrieveDropboxUsers(String configId)
      Retrieve all Dropbox team members via POST /2/team/members/list_v2, paging with /2/team/members/list/continue_v2 until has_more is false. As a side effect, each member's roles[] array is harvested into the admin-role catalog cache (see retrieveAdminRoleNameToId(String)).
      Parameters:
      configId - external system config id
      Returns:
      the list of DropboxUser objects
    • retrieveDropboxUser

      public static DropboxUser retrieveDropboxUser(String configId, String selectorTag, String selectorValue)
      Retrieve a single Dropbox member via POST /2/team/members/get_info_v2 using a UserSelectorArg union. selectorTag is the union tag (external_id, email, or team_member_id) and selectorValue is the matching value. Returns null when the member is not found (id_not_found).
      Parameters:
      configId - external system config id
      selectorTag - the union discriminator (external_id | email | team_member_id)
      selectorValue - the value to match
      Returns:
      the DropboxUser, or null if not found
    • createDropboxUser

      public static DropboxUser createDropboxUser(String configId, DropboxUser dropboxUser)
      Create (invite) a Dropbox team member via POST /2/team/members/add_v2. The response is either an inline {".tag":"complete","complete":[MemberAddV2Result]} or a deferred {".tag":"async_job_id",..}; in the deferred case this method polls /2/team/members/add/job_status/get_v2 and reads the completed result from the terminal response.
      Parameters:
      configId - external system config id
      dropboxUser - the member to create (must have email set)
      Returns:
      the created DropboxUser with team_member_id populated, or null if the result is unusable
    • updateDropboxUser

      public static DropboxUser updateDropboxUser(String configId, DropboxUser dropboxUser, Set<String> fieldNamesToSet)
      Update a Dropbox member's profile via POST /2/team/members/set_profile_v2. The member is selected by team_member_id; only the supplied field names are changed. The response is a TeamMemberInfoV2 ({"profile":..}).
      Parameters:
      configId - external system config id
      dropboxUser - the member with updated fields (must have id set)
      fieldNamesToSet - the field names to update, or null for all
      Returns:
      the updated DropboxUser
    • removeDropboxUser

      public static void removeDropboxUser(String configId, String teamMemberId, boolean wipeData, boolean keepAccount)
      Remove a Dropbox member from the team via POST /2/team/members/remove. The response is a LaunchEmptyResult which may be asynchronous; this method polls /2/team/members/remove/job_status/get to completion.
      Parameters:
      configId - external system config id
      teamMemberId - the native team_member_id to remove
      wipeData - whether to wipe the member's data on removal
      keepAccount - whether to convert the account to a free Basic account (keep) rather than delete it
    • suspendDropboxUser

      public static void suspendDropboxUser(String configId, String teamMemberId)
      Suspend a Dropbox team member (deactivate; the account stays on the team but the member loses access) via POST /2/team/members/suspend. wipe_data is false so the member's files are retained for when they are unsuspended.
      Parameters:
      configId - external system config id
      teamMemberId - the native team_member_id to suspend
    • unsuspendDropboxUser

      public static void unsuspendDropboxUser(String configId, String teamMemberId)
      Unsuspend (reactivate) a previously suspended Dropbox team member via POST /2/team/members/unsuspend.
      Parameters:
      configId - external system config id
      teamMemberId - the native team_member_id to unsuspend
    • setDropboxAdminRoles

      public static void setDropboxAdminRoles(String configId, String teamMemberId, List<String> roleIds)
      Set a Dropbox member's admin roles via POST /2/team/members/set_admin_permissions_v2. An empty roleIds list demotes the member to member_only (no admin rights). Role ids are resolved by the caller via retrieveAdminRoleNameToId(String).
      Parameters:
      configId - external system config id
      teamMemberId - the native team_member_id
      roleIds - the Dropbox role ids to assign (empty for member_only)
    • recoverDropboxUser

      public static void recoverDropboxUser(String configId, String email)
      Recover a removed Dropbox team member within the 7-day recovery window via POST /2/team/members/recover, selecting the member by email. Used by createDropboxUser(java.lang.String, edu.internet2.middleware.grouper.app.dropbox.DropboxUser) when a re-add hits the user_already_on_team recoverable conflict. Throws if recovery fails -- e.g. the account was invited-but-never-accepted, or is outside the recovery window (the caller then surfaces a normal provisioning failure).
      Parameters:
      configId - external system config id
      email - the member email to recover
    • parseIgnoreSet

      public static Set<String> parseIgnoreSet(String commaSeparated)
      Parse a comma-separated ignore string into a lowercase set for case-insensitive matching.
      Parameters:
      commaSeparated - the comma-separated string (may be null or blank)
      Returns:
      the set of lowercase trimmed values, or null if the input is blank/empty
    • isIgnored

      public static boolean isIgnored(String value, Set<String> ignoreSet)
      Check whether a value is in the ignore set (case-insensitive).
      Parameters:
      value - the value to check
      ignoreSet - the set of lowercase values to ignore
      Returns:
      true if the value should be ignored
    • main

      public static void main(String[] args)
      Manual integration entry point. Requires grouper-loader.properties configured with a "dropboxProd" WsBearerToken external system. Intended for hand-running against a real tenant; the automated tests use the mock service instead.
      Parameters:
      args - unused