UNPKG

forest-cli

Version:

The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.

150 lines 13.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const context_1 = __importDefault(require("@forestadmin/context")); const superagent_1 = __importDefault(require("superagent")); // No bulk endpoint for teams/role, so enrich per-user — bound the fan-out. const ENRICH_CONCURRENCY = 5; /** Run `fn` over `items` with at most `limit` in flight, preserving order. */ async function mapWithConcurrency(items, limit, fn) { const results = new Array(items.length); let cursor = 0; const worker = async () => { while (cursor < items.length) { const index = cursor; cursor += 1; // eslint-disable-next-line no-await-in-loop -- bounded worker: one task at a time per worker results[index] = await fn(items[index]); } }; await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker)); return results; } class UserManager { constructor(config) { this.config = config; const { assertPresent, authenticator, env, logger } = context_1.default.inject(); assertPresent({ authenticator, env, logger }); this.authenticator = authenticator; this.env = env; this.logger = logger; } headers() { return { Authorization: `Bearer ${this.authenticator.getAuthToken()}`, 'forest-project-id': String(this.config.projectId), }; } async listForProject() { const users = await this.requestUsers(); return mapWithConcurrency(users, ENRICH_CONCURRENCY, user => this.toProjectUser(user)); } /** Raw project-users list (one call, no enrichment). Failures surface to the caller. */ async requestUsers() { const response = await superagent_1.default .get(`${this.env.FOREST_SERVER_URL}/api/projects/${this.config.projectId}/users`) .set(this.headers()) .send(); return (response.body?.data ?? []); } async toProjectUser(user, strict = false) { const attributes = user.attributes ?? {}; const userId = user.id ? String(user.id) : ''; const [teams, role] = await Promise.all([ this.fetchTeams(userId, strict), this.fetchRole(userId, strict), ]); return { id: userId, email: String(attributes.email ?? ''), name: [attributes.first_name, attributes.last_name].filter(Boolean).join(' '), permissionLevel: String(attributes.permission_level ?? ''), roleId: role.id, role: role.name, teams, }; } /** * Resolve a project user by email for the edit path. Returns undefined when no * user matches; transport/API failures throw. The role/teams are read in `strict` * mode (never degraded): the edit reassigns the role and replaces the teams from * these values, so a failed read must abort the edit, not silently wipe them. */ async findByEmail(email) { const users = await this.requestUsers(); const match = users.find(user => String(user.attributes?.email ?? '') === email); if (!match) return undefined; return this.toProjectUser(match, true); } async editUser(userId, currentPermissionLevel, currentRoleId, changes) { const effectiveRoleId = changes.roleId ?? currentRoleId; // Ids are sent as integers: the server's update validator requires // `id: number` for data/role/teams/projects on this route. const relationships = { projects: { data: [{ type: 'projects', id: Number(this.config.projectId) }] }, ...(effectiveRoleId && { role: { data: { type: 'roles', id: Number(effectiveRoleId) } } }), }; if (changes.teamIds) { relationships.teams = { data: changes.teamIds.map(id => ({ type: 'teams', id: Number(id) })), }; } await superagent_1.default .put(`${this.env.FOREST_SERVER_URL}/api/users/${userId}`) .set(this.headers()) .send({ data: { type: 'users', id: Number(userId), attributes: { permission_level: changes.permissionLevel ?? currentPermissionLevel }, relationships, }, }); } // `strict` is used by the edit path: a failed read must surface (abort the edit) // rather than degrade, since the edit writes these values back. async fetchTeams(userId, strict = false) { if (!userId) return []; try { const response = await superagent_1.default .get(`${this.env.FOREST_SERVER_URL}/api/users/${userId}/teams`) .set(this.headers()) .send(); return (response.body?.data ?? []) .map(team => String(team.attributes?.name ?? '')) .filter(Boolean); } catch (error) { if (strict) throw error; this.logger.warn(`Could not fetch teams for user ${userId} (showing none): ${error}`); return []; } } async fetchRole(userId, strict = false) { if (!userId) return { id: null, name: null }; try { const response = await superagent_1.default .get(`${this.env.FOREST_SERVER_URL}/api/users`) .query({ projectId: this.config.projectId, id: userId, include: 'role' }) .set(this.headers()) .send(); const role = (response.body?.included ?? []).find(resource => resource.type === 'roles'); const name = role?.attributes?.name; return { id: role?.id ?? null, name: typeof name === 'string' ? name : null }; } catch (error) { if (strict) throw error; this.logger.warn(`Could not fetch role for user ${userId} (showing none): ${error}`); return { id: null, name: null }; } } } exports.default = UserManager; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZpY2VzL3VzZXItbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1FQUEyQztBQUMzQyw0REFBK0I7QUEyQi9CLDJFQUEyRTtBQUMzRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsQ0FBQztBQUU3Qiw4RUFBOEU7QUFDOUUsS0FBSyxVQUFVLGtCQUFrQixDQUMvQixLQUFVLEVBQ1YsS0FBYSxFQUNiLEVBQTJCO0lBRTNCLE1BQU0sT0FBTyxHQUFRLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM3QyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFFZixNQUFNLE1BQU0sR0FBRyxLQUFLLElBQW1CLEVBQUU7UUFDdkMsT0FBTyxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU0sRUFBRTtZQUM1QixNQUFNLEtBQUssR0FBRyxNQUFNLENBQUM7WUFDckIsTUFBTSxJQUFJLENBQUMsQ0FBQztZQUNaLDZGQUE2RjtZQUM3RixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsTUFBTSxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7U0FDekM7SUFDSCxDQUFDLENBQUM7SUFFRixNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBRWpGLE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxNQUFxQixXQUFXO0lBUzlCLFlBQVksTUFBc0M7UUFDaEQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsTUFBTSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsR0FBRyxFQUFFLE1B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