Skip to main content
Glama
team-manager.ts13.8 kB
import { WorkspaceManagementTool } from './workspace-tool'; import { TeamService } from '../services/team-service'; import { TallyApiClientConfig } from '../services/TallyApiClient'; import { TeamMember, TeamRole, CreateTeamRequest, UpdateTeamRequest, BulkTeamOperation, TeamMembershipUpdate, BulkMembershipUpdate, TeamPermission, TeamAccessSummary, TeamAnalytics, TeamResponse, TeamListResponse, BulkOperationResponse, OrganizationStructure, UserRole, TallySuccessResponse, } from '../models'; /** * TeamManager class handles team creation, updates, deletion, and hierarchy management. * It provides comprehensive team management functionality including nested team structures, * user assignment, permission inheritance, and organization visualization. */ export class TeamManager { private workspaceTool: WorkspaceManagementTool; private teamService: TeamService; constructor(apiClientConfig: TallyApiClientConfig = {}) { this.workspaceTool = new WorkspaceManagementTool(apiClientConfig); this.teamService = new TeamService(apiClientConfig); } // ============================================ // BASIC TEAM OPERATIONS // ============================================ /** * Create a new team within a workspace */ public async createTeam(request: CreateTeamRequest): Promise<TeamResponse> { // Validate workspace exists await this.workspaceTool.getWorkspaceDetails(request.workspaceId); return this.teamService.createTeam(request); } /** * Get a team by ID with full details */ public async getTeam(teamId: string): Promise<TeamResponse> { return this.teamService.getTeam(teamId); } /** * Update an existing team */ public async updateTeam(teamId: string, updates: UpdateTeamRequest): Promise<TeamResponse> { // If parent team is being changed (including set to undefined), validate the hierarchy if ('parentTeamId' in updates) { await this.validateTeamHierarchy(teamId, updates.parentTeamId); } return this.teamService.updateTeam(teamId, updates); } /** * Delete a team and handle member reassignment */ public async deleteTeam(teamId: string, options: { reassignMembersTo?: string; // Team ID to reassign members deleteChildTeams?: boolean; // Whether to delete child teams or move them to parent } = {}): Promise<TallySuccessResponse> { const team = await this.getTeam(teamId); // Handle member reassignment if specified if (options.reassignMembersTo && team.team.members.length > 0) { for (const member of team.team.members) { await this.addTeamMember(options.reassignMembersTo, member.userId, member.role, member.permissions); } } // Handle child teams if (team.team.childTeams.length > 0) { if (options.deleteChildTeams) { // Recursively delete child teams for (const childTeamId of team.team.childTeams) { await this.deleteTeam(childTeamId, options); } } else { // Move child teams to this team's parent for (const childTeamId of team.team.childTeams) { await this.moveTeam(childTeamId, team.team.parentTeamId); } } } return this.teamService.deleteTeam(teamId); } /** * List teams in a workspace with filtering options */ public async getTeams( workspaceId: string, options: { page?: number; limit?: number; parentTeamId?: string; includeSubteams?: boolean; sortBy?: 'name' | 'created' | 'updated' | 'memberCount'; sortOrder?: 'asc' | 'desc'; } = {} ): Promise<TeamListResponse> { // Validate workspace exists await this.workspaceTool.getWorkspaceDetails(workspaceId); return this.teamService.getTeams(workspaceId, options); } // ============================================ // TEAM MEMBERSHIP MANAGEMENT // ============================================ /** * Add a member to a team */ public async addTeamMember( teamId: string, userId: string, role: TeamRole = 'member', permissions: string[] = [] ): Promise<TeamMember> { // Validate team exists await this.getTeam(teamId); return this.teamService.addTeamMember(teamId, userId, role, permissions); } /** * Remove a member from a team */ public async removeTeamMember(teamId: string, userId: string): Promise<TallySuccessResponse> { return this.teamService.removeTeamMember(teamId, userId); } /** * Update a team member's role or permissions */ public async updateTeamMember( teamId: string, userId: string, updates: { role?: TeamRole; permissions?: string[] } ): Promise<TeamMember> { return this.teamService.updateTeamMember(teamId, userId, updates); } /** * Bulk update team memberships */ public async bulkUpdateMemberships(bulkUpdate: BulkMembershipUpdate): Promise<BulkOperationResponse> { return this.teamService.bulkUpdateMemberships(bulkUpdate); } /** * Move users between teams in bulk */ public async moveUsersBetweenTeams( userIds: string[], fromTeamId: string, toTeamId: string, newRole?: TeamRole ): Promise<BulkOperationResponse> { const updates: TeamMembershipUpdate[] = userIds.map(userId => ({ userId, action: 'remove' as const, })); // Remove from source team await this.bulkUpdateMemberships({ teamId: fromTeamId, updates }); // Add to destination team const addUpdates: TeamMembershipUpdate[] = userIds.map(userId => ({ userId, role: newRole || 'member', action: 'add' as const, })); return this.bulkUpdateMemberships({ teamId: toTeamId, updates: addUpdates }); } // ============================================ // TEAM HIERARCHY MANAGEMENT // ============================================ /** * Move a team to a new parent (or make it a root team) */ public async moveTeam(teamId: string, newParentTeamId?: string): Promise<TeamResponse> { // Validate hierarchy constraints await this.validateTeamHierarchy(teamId, newParentTeamId); // Update the team's parent return this.teamService.updateTeam(teamId, { parentTeamId: newParentTeamId }); } /** * Get the organization structure for a workspace */ public async getOrganizationStructure(workspaceId: string): Promise<OrganizationStructure> { // Validate workspace exists await this.workspaceTool.getWorkspaceDetails(workspaceId); return this.teamService.getOrganizationStructure(workspaceId); } /** * Create a nested team structure from a hierarchy definition */ public async createTeamHierarchy( workspaceId: string, hierarchy: { name: string; description?: string; children?: Array<{ name: string; description?: string; children?: any[] }>; } ): Promise<{ rootTeam: TeamResponse; createdTeams: TeamResponse[] }> { const createdTeams: TeamResponse[] = []; // Create root team const rootTeam = await this.createTeam({ name: hierarchy.name, description: hierarchy.description, workspaceId, }); createdTeams.push(rootTeam); // Recursively create child teams if (hierarchy.children) { await this.createChildTeams(rootTeam.team.id, hierarchy.children, workspaceId, createdTeams); } return { rootTeam, createdTeams }; } /** * Get team hierarchy path from root to specified team */ public async getTeamPath(teamId: string): Promise<TeamResponse[]> { const path: TeamResponse[] = []; let currentTeamId: string | undefined = teamId; while (currentTeamId) { const team = await this.getTeam(currentTeamId); path.unshift(team); currentTeamId = team.team.parentTeamId; } return path; } /** * Get all descendant teams (children, grandchildren, etc.) */ public async getTeamDescendants(teamId: string): Promise<TeamResponse[]> { const descendants: TeamResponse[] = []; const team = await this.getTeam(teamId); for (const childId of team.team.childTeams) { const child = await this.getTeam(childId); descendants.push(child); // Recursively get descendants const childDescendants = await this.getTeamDescendants(childId); descendants.push(...childDescendants); } return descendants; } // ============================================ // PERMISSION AND ACCESS MANAGEMENT // ============================================ /** * Set team-based permissions for resources */ public async setTeamPermission(permission: TeamPermission): Promise<TeamPermission> { return this.teamService.setTeamPermission(permission); } /** * Get effective permissions for a team member */ public async getTeamMemberPermissions(teamId: string, userId: string): Promise<TeamAccessSummary> { return this.teamService.getTeamMemberPermissions(teamId, userId); } /** * Inherit workspace permissions to team level */ public async inheritWorkspacePermissions( workspaceId: string, teamId: string ): Promise<{ success: boolean; permissionsInherited: number }> { const workspace = await this.workspaceTool.getWorkspaceDetails(workspaceId); const team = await this.getTeam(teamId); // Map workspace roles to team permissions let permissionsInherited = 0; if (workspace.members) { for (const member of workspace.members) { // Add workspace members to team if they're not already members const isTeamMember = team.team.members.some(tm => tm.userId === member.id); if (!isTeamMember) { const teamRole = this.mapWorkspaceRoleToTeamRole(member.role); await this.addTeamMember(teamId, member.id, teamRole); permissionsInherited++; } } } return { success: true, permissionsInherited }; } // ============================================ // ANALYTICS AND INSIGHTS // ============================================ /** * Get team analytics and insights */ public async getTeamAnalytics(teamId: string): Promise<TeamAnalytics> { return this.teamService.getTeamAnalytics(teamId); } /** * Get teams a user belongs to */ public async getUserTeams(workspaceId: string, userId: string): Promise<TeamResponse[]> { return this.teamService.getUserTeams(workspaceId, userId); } /** * Check if a user can perform an action on a team */ public async canUserPerformAction( teamId: string, userId: string, action: 'view' | 'edit' | 'manage' | 'delete' ): Promise<{ canPerform: boolean; reason?: string }> { return this.teamService.canUserPerformAction(teamId, userId, action); } // ============================================ // BULK OPERATIONS // ============================================ /** * Perform bulk operations on multiple teams */ public async bulkTeamOperation(operation: BulkTeamOperation): Promise<BulkOperationResponse> { return this.teamService.bulkTeamOperation(operation); } /** * Reorganize teams by moving multiple teams at once */ public async reorganizeTeams( moves: Array<{ teamId: string; newParentTeamId?: string }> ): Promise<BulkOperationResponse> { const results: Array<{ teamId: string; success: boolean; data?: any }> = []; for (const move of moves) { try { await this.moveTeam(move.teamId, move.newParentTeamId); results.push({ teamId: move.teamId, success: true }); } catch (error) { results.push({ teamId: move.teamId, success: false, data: { error: error instanceof Error ? error.message : 'Unknown error' } }); } } const successCount = results.filter(r => r.success).length; return { success: successCount > 0, processedCount: results.length, failedCount: results.length - successCount, results, }; } // ============================================ // HELPER METHODS // ============================================ /** * Validate team hierarchy constraints (prevent circular references) */ private async validateTeamHierarchy(teamId: string, newParentTeamId?: string): Promise<void> { if (!newParentTeamId) return; // Moving to root is always valid // Check if the new parent is a descendant of the team being moved const descendants = await this.getTeamDescendants(teamId); const isCircular = descendants.some(desc => desc.team.id === newParentTeamId); if (isCircular) { throw new Error('Cannot move team: would create circular reference in hierarchy'); } // Validate that the parent team exists await this.getTeam(newParentTeamId); } /** * Recursively create child teams */ private async createChildTeams( parentTeamId: string, children: Array<{ name: string; description?: string; children?: any[] }>, workspaceId: string, createdTeams: TeamResponse[] ): Promise<void> { for (const child of children) { const childTeam = await this.createTeam({ name: child.name, description: child.description, workspaceId, parentTeamId, }); createdTeams.push(childTeam); if (child.children) { await this.createChildTeams(childTeam.team.id, child.children, workspaceId, createdTeams); } } } /** * Map workspace roles to team roles */ private mapWorkspaceRoleToTeamRole(workspaceRole: UserRole): TeamRole { const roleMap: Record<UserRole, TeamRole> = { 'owner': 'team_lead', 'admin': 'team_lead', 'member': 'member', }; return roleMap[workspaceRole] || 'member'; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/learnwithcc/tally-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server