From f27212f60e6c73979d689e8332729b279b71520f Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 5 Dec 2021 20:57:15 +0100 Subject: [PATCH] feat(api): user store now handels deleted users. `.users` now is a getter that filters out deleted users. For all users, including deleted, use `_users` property (should not be needed as `getUser` will return the needed information as well). --- api/src/stores/user.ts | 129 +++++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/api/src/stores/user.ts b/api/src/stores/user.ts index 41bc2a2..96d1d4b 100644 --- a/api/src/stores/user.ts +++ b/api/src/stores/user.ts @@ -11,81 +11,134 @@ export const useUserStore = defineStore({ state: () => ({ roles: [] as FG.Role[], - users: [] as FG.User[], permissions: [] as FG.Permission[], - _dirty_users: true, - _dirty_roles: true, + // list of all users, include deleted ones, use `users` getter for list of active ones + _users: [] as FG.User[], + // Internal flags for deciding if lists need to force-loaded }), - getters: {}, + getters: { + users(state) { + return state._users.filter((u) => !u.deleted); + }, + }, actions: { + /** Simply filter all users by ID */ findUser(userid: string) { - return this.users.find((user) => user.userid === userid); + return this._users.find((user) => user.userid === userid); }, + + /** Retrieve user by ID + * @param userid ID of user to retrieve + * @param force If set to true the user is loaded from backend even when a local copy is available + * @returns Retrieved user (Promise) or raise an error + * @throws Probably an AxiosError if loading failed + */ async getUser(userid: string, force = false) { - const idx = this.users.findIndex((user) => user.userid === userid); - if (force || this._dirty_users || idx === -1) { + const idx = this._users.findIndex((user) => user.userid === userid); try { const { data } = await api.get(`/users/${userid}`); fixUser(data); - if (idx === -1) this.users.push(data); - else this.users[idx] = data; + if (idx === -1) this._users.push(data); + else this._users[idx] = data; return data; } catch (error) { // Ignore 404, throw all other if (!isAxiosError(error, 404)) throw error; } } else { - return this.users[idx]; + return this._users[idx]; } }, + /** Retrieve list of all users + * @param force If set to true a fresh users list is loaded from backend even when a local copy is available + * @returns Array of retrieved users (Promise) + * @throws Probably an AxiosError if loading failed + */ async getUsers(force = false) { if (force || this._dirty_users) { const { data } = await api.get('/users'); data.forEach(fixUser); - this.users = data; - this._dirty_users = false; + this._users = data; } - return this.users; + return this._users; }, + /** Save modifications of user on backend + * @param user Modified user to save + * @throws Probably an AxiosError if request failed (404 = Invalid userid, 400 = Invalid data) + */ async updateUser(user: FG.User) { await api.put(`/users/${user.userid}`, user); - + // Modifcation accepted by backend + // Save modifications back to our users list + const idx = this._users.findIndex((u) => u.userid === user.userid); + if (idx > -1) this._users[idx] = user; + // If user was current user, save modifications back to the main store const mainStore = useMainStore(); if (user.userid === mainStore.user?.userid) mainStore.user = user; - this._dirty_users = true; }, + /** Register a new user + * @param user User to register (id not set) + * @returns The registered user (id set) + * @throws Probably an AxiosError if request failed + */ async createUser(user: FG.User) { const { data } = await api.post('/users', user); - this.users.push(data); + this._users.push(fixUser(data)); return data; }, + /** Delete an user + * Throws if failed and resolves void if succeed + * + * @param user User or ID of user to delete + * @throws Probably an AxiosError if request failed + */ async deleteUser(user: FG.User | string) { if (typeof user === 'object') user = user.userid; - + await api.delete(`/users/${user}`); - this.users = this.users.filter(u => u.userid != user); + this._users = this._users.filter((u) => u.userid != user); }, - async uploadAvatar(user: FG.User, file: string | File) { + /** Upload an avatar for an user + * Throws if failed and resolves void if succeed + * + * @param user User or ID of user + * @param file Avatar file to upload + * @throws Probably an AxiosError if request failed + */ + async uploadAvatar(user: FG.User | string, file: string | File) { + if (typeof user === 'object') user = user.userid; + const formData = new FormData(); formData.append('file', file); - await api.post(`/users/${user.userid}/avatar`, formData, { + await api.post(`/users/${user}/avatar`, formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); }, - async deleteAvatar(user: FG.User) { - await api.delete(`/users/${user.userid}/avatar`); + /** Delete avatar of an user + * @param user User or ID of user + * @throws Probably an AxiosError if request failed + */ + async deleteAvatar(user: FG.User | string) { + if (typeof user === 'object') user = user.userid; + + await api.delete(`/users/${user}/avatar`); }, + /** Retrieve list of all permissions + * @param force If set to true a fresh list is loaded from backend even when a local copy is available + * @returns Array of retrieved permissions (Promise) + * @throws Probably an AxiosError if request failed + */ async getPermissions(force = false) { if (force || this.permissions.length === 0) { const { data } = await api.get('/roles/permissions'); @@ -94,6 +147,11 @@ export const useUserStore = defineStore({ return this.permissions; }, + /** Retrieve list of all roles + * @param force If set to true a fresh list is loaded from backend even when a local copy is available + * @returns Array of retrieved roles (Promise) + * @throws Probably an AxiosError if request failed + */ async getRoles(force = false) { if (force || this._dirty_roles) { const { data } = await api.get('/roles'); @@ -103,30 +161,37 @@ export const useUserStore = defineStore({ return this.roles; }, + /** Save modifications of role on the backend + * @param role role to save + * @throws Probably an AxiosError if request failed + */ async updateRole(role: FG.Role) { - try { - await api.put(`/roles/${role.id}`, role); - } catch (error) { - console.warn(error); - } - this._updatePermission(role); - }, + await api.put(`/roles/${role.id}`, role); - _updatePermission(role: FG.Role) { const idx = this.roles.findIndex((r) => r.id === role.id); if (idx != -1) this.roles[idx] = role; this._dirty_roles = true; }, + /** Create a new role + * @param role Role to create (ID not set) + * @returns Created role (ID set) + * @throws Probably an AxiosError if request failed + */ async newRole(role: FG.Role) { const { data } = await api.post('/roles', role); this.roles.push(data); return data; }, + /** Delete a role + * @param role Role or ID of role to delete + * @throws Probably an AxiosError if request failed (409 if role still in use) + */ async deleteRole(role: FG.Role | number) { - await api.delete(`/roles/${typeof role === 'number' ? role : role.id}`); - this.roles = this.roles.filter((r) => r.id !== (typeof role == 'number' ? role : role.id)); + if (typeof role === 'object') role = role.id; + await api.delete(`/roles/${role}`); + this.roles = this.roles.filter((r) => r.id !== role); }, }, });