diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7667c3d..0a8acdce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Upgraded to PostgreSQL 15 and PostGIS 3.4. - Removed unused `IsViewer` flag from user. - Removed unused `UserEvent` from user. +- Migrated `User` API endpoints to .NET API. ### Fixed diff --git a/src/api-legacy/__init__.py b/src/api-legacy/__init__.py index 22fc05f9c..cbde09dff 100644 --- a/src/api-legacy/__init__.py +++ b/src/api-legacy/__init__.py @@ -62,7 +62,6 @@ # User actions from bms.v1.user.handler import UserHandler -from bms.v1.user.admin import AdminHandler # Workgroup actions from bms.v1.user.workgrpup.admin import WorkgroupAdminHandler diff --git a/src/api-legacy/main.py b/src/api-legacy/main.py index 77effce35..6865fe830 100644 --- a/src/api-legacy/main.py +++ b/src/api-legacy/main.py @@ -88,7 +88,6 @@ async def close(application): # user handlers SettingHandler, UserHandler, - AdminHandler, WorkgroupAdminHandler, # Borehole handlers @@ -132,7 +131,6 @@ async def close(application): # User handlers (r'/api/v1/user', UserHandler), - (r'/api/v1/user/edit', AdminHandler), (r'/api/v1/user/workgroup/edit', WorkgroupAdminHandler), diff --git a/src/api-legacy/v1/user/__init__.py b/src/api-legacy/v1/user/__init__.py deleted file mode 100644 index 91fcf5553..000000000 --- a/src/api-legacy/v1/user/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- - -from bms.v1.user.delete import DeleteUser -from bms.v1.user.disable import DisableUser -from bms.v1.user.enable import EnableUser -from bms.v1.user.update import UpdateUser diff --git a/src/api-legacy/v1/user/admin.py b/src/api-legacy/v1/user/admin.py deleted file mode 100644 index feb678da6..000000000 --- a/src/api-legacy/v1/user/admin.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -from bms import ( - AuthorizationException -) -from bms.v1.handlers.admin import Admin -from bms.v1.user import ( - DeleteUser, - DisableUser, - EnableUser, - UpdateUser -) - - -class AdminHandler(Admin): - async def execute(self, request): - - action = request.pop('action', None) - - if action in [ - 'CREATE', - 'DISABLE', - 'DELETE', - 'ENABLE', - 'UPDATE' - ]: - - async with self.pool.acquire() as conn: - - exe = None - - if action in [ - 'CREATE', - 'DELETE', - 'DISABLE', - 'ENABLE', - 'UPDATE' - ]: - if self.user['admin'] is False: - raise AuthorizationException() - - # Admin user cannot remove his own admin flag - if ( - action == 'UPDATE' and - self.user['id'] == request['user_id'] - ): - was_admin = await conn.fetchval(""" - SELECT admin_usr - FROM - bdms.users - WHERE - id_usr = $1 - """, request['user_id']) - - if was_admin and request['admin'] is False: - request['admin'] = True - - if action == 'UPDATE': - exe = UpdateUser(conn) - - elif action == 'DISABLE': - exe = DisableUser(conn) - - elif action == 'ENABLE': - exe = EnableUser(conn) - - elif action == 'DELETE': - exe = DeleteUser(conn) - - if exe is not None: - return ( - await exe.execute(**request) - ) - - raise Exception("Action '%s' unknown" % action) diff --git a/src/api-legacy/v1/user/delete.py b/src/api-legacy/v1/user/delete.py deleted file mode 100644 index 72bb0d1ff..000000000 --- a/src/api-legacy/v1/user/delete.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.action import Action - - -class DeleteUser(Action): - - async def execute(self, id): - - # Check if user has done contributions - contributions = await self.conn.fetchval(""" - SELECT - ( - COALESCE(workflows, 0) - + COALESCE(layers, 0) - + COALESCE(borehole_author, 0) - + COALESCE(borehole_updater, 0) - + COALESCE(borehole_locker, 0) - + COALESCE(stratigraphy_author, 0) - + COALESCE(stratigraphy_updater, 0) - ) as contributions - - FROM - bdms.users - - LEFT JOIN ( - SELECT - id_usr_fk, - count(id_usr_fk) as workflows - FROM - bdms.workflow - GROUP BY - id_usr_fk - ) as wf - ON id_usr = wf.id_usr_fk - - LEFT JOIN ( - SELECT - creator_lay, - count(creator_lay) as layers - FROM - bdms.layer - GROUP BY - creator_lay - ) as ly - ON id_usr = ly.creator_lay - - LEFT JOIN ( - SELECT - created_by_bho, - count(created_by_bho) as borehole_author - FROM - bdms.borehole - GROUP BY - created_by_bho - ) as bha - ON id_usr = bha.created_by_bho - - LEFT JOIN ( - SELECT - updated_by_bho, - count(updated_by_bho) as borehole_updater - FROM - bdms.borehole - GROUP BY - updated_by_bho - ) as bhu - ON id_usr = bhu.updated_by_bho - - LEFT JOIN ( - SELECT - locked_by_bho, - count(locked_by_bho) as borehole_locker - FROM - bdms.borehole - GROUP BY - locked_by_bho - ) as bhl - ON id_usr = bhl.locked_by_bho - - LEFT JOIN ( - SELECT - author_sty, - count(author_sty) as stratigraphy_author - FROM - bdms.stratigraphy - GROUP BY - author_sty - ) as stc - ON id_usr = stc.author_sty - - LEFT JOIN ( - SELECT - updater_sty, - count(updater_sty) as stratigraphy_updater - FROM - bdms.stratigraphy - GROUP BY - updater_sty - ) as stu - ON id_usr = stu.updater_sty - - WHERE id_usr = $1 - """, id) - - if contributions > 0: - raise Exception( - f"User cannot be deleted because of {contributions} contributions" - ) - - await self.conn.execute(""" - DELETE FROM bdms.users - WHERE id_usr = $1 - """, id) - - return None diff --git a/src/api-legacy/v1/user/disable.py b/src/api-legacy/v1/user/disable.py deleted file mode 100644 index 3e4c14c28..000000000 --- a/src/api-legacy/v1/user/disable.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.action import Action - - -class DisableUser(Action): - - async def execute(self, id): - return { - "id": ( - await self.conn.fetchval(""" - UPDATE - bdms.users - - SET - disabled_usr = now() - - WHERE - id_usr = $1 - """, - id - ) - ) - } diff --git a/src/api-legacy/v1/user/enable.py b/src/api-legacy/v1/user/enable.py deleted file mode 100644 index 02d333a60..000000000 --- a/src/api-legacy/v1/user/enable.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.action import Action - - -class EnableUser(Action): - - async def execute(self, id): - return { - "id": ( - await self.conn.fetchval(""" - UPDATE - bdms.users - - SET - disabled_usr = NULL - - WHERE - id_usr = $1 - """, - id - ) - ) - } diff --git a/src/api-legacy/v1/user/handler.py b/src/api-legacy/v1/user/handler.py index 6080d7086..c217df755 100644 --- a/src/api-legacy/v1/user/handler.py +++ b/src/api-legacy/v1/user/handler.py @@ -11,7 +11,7 @@ async def execute(self, request): action = request.pop('action', None) if action in [ - 'GET', 'RELOAD' + 'GET' ]: workgroups = [] diff --git a/src/api-legacy/v1/user/update.py b/src/api-legacy/v1/user/update.py deleted file mode 100644 index 65a339fea..000000000 --- a/src/api-legacy/v1/user/update.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.action import Action -from bms.v1.exceptions import DuplicateException - - -class UpdateUser(Action): - - async def execute( - self, user_id, admin = False - ): - - return { - "id": ( - await self.conn.fetchval(""" - UPDATE - bdms.users - - SET - admin_usr = $1 - - WHERE - id_usr = $2 - """, - admin, - user_id - ) - ) - } diff --git a/src/api/BdmsContext.cs b/src/api/BdmsContext.cs index be5b35619..e700a9062 100644 --- a/src/api/BdmsContext.cs +++ b/src/api/BdmsContext.cs @@ -18,6 +18,11 @@ public class BdmsContext : DbContext public DbSet Stratigraphies { get; set; } public DbSet Terms { get; set; } public DbSet Users { get; set; } + public IQueryable UsersWithIncludes => Users + .Include(u => u.WorkgroupRoles) + .ThenInclude(wr => wr.Workgroup) + .Include(u => u.TermsAccepted) + .ThenInclude(ta => ta.Term); public DbSet UserWorkgroupRoles { get; set; } public DbSet Workflows { get; set; } public DbSet Workgroups { get; set; } @@ -88,6 +93,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("bdms"); modelBuilder.Entity().HasKey(k => new { k.UserId, k.WorkgroupId, k.Role }); + modelBuilder.Entity().HasKey(k => new { k.UserId, k.TermId }); modelBuilder.Entity() .HasMany(b => b.Files) diff --git a/src/api/BoreholeLockService.cs b/src/api/BoreholeLockService.cs index e91d9b531..ad48f3dbc 100644 --- a/src/api/BoreholeLockService.cs +++ b/src/api/BoreholeLockService.cs @@ -33,7 +33,7 @@ public async Task IsBoreholeLockedAsync(int? boreholeId, string? subjectId .Select(w => w.Role) .ToHashSet(); - if (!user.WorkgroupRoles.Any(x => x.WorkgroupId == borehole.WorkgroupId && boreholeWorkflowRoles.Contains(x.Role))) + if (user.WorkgroupRoles == null || !user.WorkgroupRoles.Any(x => x.WorkgroupId == borehole.WorkgroupId && boreholeWorkflowRoles.Contains(x.Role))) { logger.LogWarning("Current user with subject_id <{SubjectId}> does not have the required role to edit the borehole with id <{BoreholeId}>.", subjectId, boreholeId); return true; diff --git a/src/api/Controllers/BoreholeController.cs b/src/api/Controllers/BoreholeController.cs index 15998c423..dcc31acab 100644 --- a/src/api/Controllers/BoreholeController.cs +++ b/src/api/Controllers/BoreholeController.cs @@ -38,7 +38,7 @@ public async Task> CopyAsync([Required] int id, [Required] int .SingleOrDefaultAsync(u => u.SubjectId == HttpContext.GetUserSubjectId()) .ConfigureAwait(false); - if (user == null || !user.WorkgroupRoles.Any(w => w.WorkgroupId == workgroupId && w.Role == Role.Editor)) + if (user == null || user.WorkgroupRoles == null || !user.WorkgroupRoles.Any(w => w.WorkgroupId == workgroupId && w.Role == Role.Editor)) { return Unauthorized(); } diff --git a/src/api/Controllers/UserController.cs b/src/api/Controllers/UserController.cs index 6eee3fbba..1e10f246a 100644 --- a/src/api/Controllers/UserController.cs +++ b/src/api/Controllers/UserController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Swashbuckle.AspNetCore.Annotations; namespace BDMS.Controllers; @@ -11,33 +12,60 @@ namespace BDMS.Controllers; public class UserController : ControllerBase { private readonly BdmsContext context; + private ILogger logger; /// /// Initializes a new instance of the class. /// /// The EF database context containing data for the BDMS application. - public UserController(BdmsContext context) + /// The logger used by the controller. + public UserController(BdmsContext context, ILogger logger) { this.context = context; + this.logger = logger; } /// - /// Gets the current authenticated and authorized bdms user. + /// Gets the currently authenticated and authorized bdms user. /// [HttpGet("self")] [Authorize(Policy = PolicyNames.Viewer)] - public async Task> GetUserInformationAsync() => - await context.Users.SingleOrDefaultAsync(u => u.SubjectId == HttpContext.GetUserSubjectId()).ConfigureAwait(false); + [SwaggerResponse(StatusCodes.Status200OK, "Returns the currently logged in user.")] + public async Task GetSelf() + { + var user = await context.UsersWithIncludes.SingleOrDefaultAsync(u => u.SubjectId == HttpContext.GetUserSubjectId()).ConfigureAwait(false); + user.Deletable = IsDeletable(user); + return user; + } /// - /// Gets the user list. + /// Gets the user with the specified . + /// + [HttpGet("{id}")] + [SwaggerResponse(StatusCodes.Status200OK, "Returns the user with the specified id.")] + [SwaggerResponse(StatusCodes.Status404NotFound, "The user could not be found.")] + public async Task GetUserById(int id) + { + var user = await context.UsersWithIncludes.SingleOrDefaultAsync(u => u.Id == id).ConfigureAwait(false); + + if (user == null) + { + return NotFound(); + } + + user.Deletable = IsDeletable(user); + return Ok(user); + } + + /// + /// Gets a list of users. /// [HttpGet] + [SwaggerResponse(StatusCodes.Status200OK, "Returns a list of users.")] public async Task> GetAll() { var users = await context - .Users - .Include(x => x.WorkgroupRoles) + .UsersWithIncludes .AsNoTracking() .OrderBy(x => x.Name) .ToListAsync() @@ -45,22 +73,17 @@ public async Task> GetAll() foreach (var user in users) { - user.Deletable = !(context.Workflows.Any(x => x.UserId == user.Id) - || context.Layers.Any(x => x.CreatedById == user.Id) - || context.Layers.Any(x => x.UpdatedById == user.Id) - || context.Boreholes.Any(x => x.UpdatedById == user.Id) - || context.Boreholes.Any(x => x.CreatedById == user.Id) - || context.Boreholes.Any(x => x.LockedById == user.Id) - || context.Stratigraphies.Any(x => x.CreatedById == user.Id) - || context.Stratigraphies.Any(x => x.UpdatedById == user.Id) - || context.Files.Any(x => x.CreatedById == user.Id) - || context.BoreholeFiles.Any(x => x.UserId == user.Id)); + user.Deletable = IsDeletable(user); } return users; } + /// + /// Resets all settings to initial state. + /// [HttpPost("resetAllSettings")] + [SwaggerResponse(StatusCodes.Status200OK, "All settings were reset successfully.")] public ActionResult ResetAllSettings() { // Reset admin settings to initial state @@ -72,4 +95,96 @@ public ActionResult ResetAllSettings() return Ok(); } + + /// + /// Updates the . + /// + [HttpPut] + [SwaggerResponse(StatusCodes.Status200OK, "The user was updated successfully.")] + [SwaggerResponse(StatusCodes.Status400BadRequest, "The user could not be updated due to invalid input.")] + [SwaggerResponse(StatusCodes.Status404NotFound, "The user could not be found.")] + [SwaggerResponse(StatusCodes.Status401Unauthorized, "The current user is not authorized to update users.")] + [SwaggerResponse(StatusCodes.Status500InternalServerError, "The server encountered an unexpected condition that prevented it from fulfilling the request. ")] + public async Task Edit(User user) + { + try + { + if (user == null) + { + return BadRequest(); + } + + var userToEdit = await context.Users.SingleOrDefaultAsync(u => u.Id == user.Id).ConfigureAwait(false); + if (userToEdit == null) + { + return NotFound(); + } + + if (userToEdit.SubjectId != HttpContext.GetUserSubjectId()) + { + userToEdit.IsAdmin = user.IsAdmin; + } + + userToEdit.DisabledAt = user.DisabledAt; + + await context.SaveChangesAsync().ConfigureAwait(false); + return await GetUserById(userToEdit.Id).ConfigureAwait(false); + } + catch (Exception e) + { + var message = "Error while updating user."; + logger.LogError(e, message); + return Problem(message); + } + } + + /// + /// Deletes the user with the specified . + /// + [HttpDelete("{id}")] + [SwaggerResponse(StatusCodes.Status200OK, "The user was deleted successfully.")] + [SwaggerResponse(StatusCodes.Status400BadRequest, "The user could not be updated due to invalid input.")] + [SwaggerResponse(StatusCodes.Status404NotFound, "The user could not be found.")] + [SwaggerResponse(StatusCodes.Status401Unauthorized, "The current user is not authorized to delete users.")] + [SwaggerResponse(StatusCodes.Status500InternalServerError, "The server encountered an unexpected condition that prevented it from fulfilling the request. ")] + public async Task Delete(int id) + { + try + { + var user = await context.Users.SingleOrDefaultAsync(u => u.Id == id).ConfigureAwait(false); + if (user == null) + { + return NotFound(); + } + + if (!IsDeletable(user)) + { + return Problem("The user is associated with boreholes, layers, stratigraphies, workflows, files or borehole files and cannot be deleted."); + } + + context.Users.Remove(user); + await context.SaveChangesAsync().ConfigureAwait(false); + return Ok(); + } + catch (Exception e) + { + var message = "Error while deleting user."; + logger.LogError(e, message); + return Problem(message); + } + } + + private bool IsDeletable(User user) + { + return !(context.Workflows.Any(x => x.UserId == user.Id) + || context.Layers.Any(x => x.CreatedById == user.Id) + || context.Layers.Any(x => x.UpdatedById == user.Id) + || context.Boreholes.Any(x => x.UpdatedById == user.Id) + || context.Boreholes.Any(x => x.CreatedById == user.Id) + || context.Boreholes.Any(x => x.LockedById == user.Id) + || context.Stratigraphies.Any(x => x.CreatedById == user.Id) + || context.Stratigraphies.Any(x => x.UpdatedById == user.Id) + || context.Files.Any(x => x.CreatedById == user.Id) + || context.BoreholeFiles.Any(x => x.UserId == user.Id)); + } } diff --git a/src/api/Models/TermsAccepted.cs b/src/api/Models/TermsAccepted.cs new file mode 100644 index 000000000..3e937b770 --- /dev/null +++ b/src/api/Models/TermsAccepted.cs @@ -0,0 +1,38 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace BDMS.Models; + +/// +/// Represents a terms_accepted entity in the database. +/// +[Table("terms_accepted")] +public class TermsAccepted +{ + /// + /// Gets or sets the foreign key to the entity. + /// + [Column("id_usr_fk")] + public int UserId { get; set; } + + /// + /// Gets or sets the . + /// + public User? User { get; set; } + + /// + /// Gets or sets the foreign key to the entity. + /// + [Column("id_tes_fk")] + public int TermId { get; set; } + + /// + /// Gets or sets the . + /// + public Term? Term { get; set; } + + /// + /// Gets or sets the timestamp from the moment the terms got accepted. + /// + [Column("accepted_tea")] + public DateTime AcceptedAt { get; set; } +} diff --git a/src/api/Models/User.cs b/src/api/Models/User.cs index 77a3f71bf..6f4811ca3 100644 --- a/src/api/Models/User.cs +++ b/src/api/Models/User.cs @@ -55,10 +55,27 @@ public class User : IIdentifyable [Column("disabled_usr")] public DateTime? DisabledAt { get; set; } + /// + /// Gets or sets the timestamp from the moment a got disabled. + /// + [Column("created_usr")] + public DateTime? CreatedAt { get; set; } + + /// + /// Gets or sets the s settings. + /// + [Column("settings_usr")] + public string? Settings { get; set; } + /// /// Gets the WorkgroupRoles. /// - public IEnumerable WorkgroupRoles { get; } + public IEnumerable? WorkgroupRoles { get; } + + /// + /// Gets the TermsAccepted. + /// + public IEnumerable? TermsAccepted { get; } /// /// Gets or sets whether this user can be deleted. diff --git a/src/api/Models/UserWorkgroupRole.cs b/src/api/Models/UserWorkgroupRole.cs index 9c174769c..7d652846d 100644 --- a/src/api/Models/UserWorkgroupRole.cs +++ b/src/api/Models/UserWorkgroupRole.cs @@ -14,12 +14,22 @@ public class UserWorkgroupRole [Column("id_usr_fk")] public int UserId { get; set; } + /// + /// Gets or sets the . + /// + public User? User { get; set; } + /// /// Gets or sets the foreign key to the entity. /// [Column("id_wgp_fk")] public int WorkgroupId { get; set; } + /// + /// Gets or sets the . + /// + public Workgroup? Workgroup { get; set; } + /// /// Gets or sets the . /// diff --git a/src/client/src/api-lib/actions/user.js b/src/client/src/api-lib/actions/user.js index 754a63e5f..d676e678e 100644 --- a/src/client/src/api-lib/actions/user.js +++ b/src/client/src/api-lib/actions/user.js @@ -20,38 +20,3 @@ export function loadUser() { type: "GET", }); } - -export function reloadUser() { - return fetch("/user", { - type: "RELOAD", - }); -} - -export function updateUser(id, admin = false) { - return fetch("/user/edit", { - action: "UPDATE", - user_id: id, - admin: admin, - }); -} - -export function enableUser(id) { - return fetch("/user/edit", { - action: "ENABLE", - id: id, - }); -} - -export function disableUser(id) { - return fetch("/user/edit", { - action: "DISABLE", - id: id, - }); -} - -export function deleteUser(id) { - return fetch("/user/edit", { - action: "DELETE", - id: id, - }); -} diff --git a/src/client/src/api-lib/index.js b/src/client/src/api-lib/index.js index ca7baac8d..612a288de 100644 --- a/src/client/src/api-lib/index.js +++ b/src/client/src/api-lib/index.js @@ -4,41 +4,32 @@ import { loadSettings, patchSettings } from "./actions/settings"; import { acceptTerms, draftTerms, getTerms, getTermsDraft, publishTerms } from "./actions/terms"; -import { - setAuthentication, - unsetAuthentication, - loadUser, - reloadUser, - updateUser, - disableUser, - deleteUser, - enableUser, -} from "./actions/user"; +import { loadUser, setAuthentication, unsetAuthentication } from "./actions/user"; import { createWorkgroup, - enableWorkgroup, - disableWorkgroup, deleteWorkgroup, + disableWorkgroup, + enableWorkgroup, listWorkgroups, setRole, updateWorkgroup, } from "./actions/workgroups"; import { - updateBorehole, + createBorehole, + deleteBorehole, + deleteBoreholes, + getdBoreholeIds, + getGeojson, loadBorehole, loadBoreholes, - getdBoreholeIds, loadEditingBoreholes, - createBorehole, lockBorehole, - unlockBorehole, - deleteBorehole, - deleteBoreholes, patchBorehole, patchBoreholes, - getGeojson, + unlockBorehole, + updateBorehole, } from "./actions/borehole"; import { addIdentifier, removeIdentifier } from "./actions/identifier"; @@ -46,10 +37,10 @@ import { addIdentifier, removeIdentifier } from "./actions/identifier"; import { loadWorkflows, patchWorkflow, - updateWorkflow, - submitWorkflow, rejectWorkflow, resetWorkflow, + submitWorkflow, + updateWorkflow, } from "./actions/workflow"; import { createLayer, deleteLayer, gapLayer } from "./actions/stratigraphy"; @@ -60,7 +51,7 @@ import { loadDomains, patchCodeConfig } from "./actions/domains"; import { getWms } from "./actions/geoapi"; -import store, { injectReducer, configureStore, createReducer } from "./reducers"; +import store, { configureStore, createReducer, injectReducer } from "./reducers"; export { getHeight, @@ -74,11 +65,6 @@ export { setAuthentication, unsetAuthentication, loadUser, - reloadUser, - updateUser, - disableUser, - deleteUser, - enableUser, createWorkgroup, enableWorkgroup, disableWorkgroup, diff --git a/src/client/src/api/apiInterfaces.ts b/src/client/src/api/apiInterfaces.ts new file mode 100644 index 000000000..5552b554e --- /dev/null +++ b/src/client/src/api/apiInterfaces.ts @@ -0,0 +1,60 @@ +export enum Role { + View, + Editor, + Controller, + Validator, + Publisher, +} + +export interface Workgroup { + // TODO: Add boreholes + id: number; + name: string; + created?: Date; + disabled?: Date; + settings?: string; + isSupplier?: boolean; +} + +export interface WorkgroupRole { + userId: number; + user: User; + workgroupId: number; + workgroup: Workgroup; + role: Role; +} + +export interface Term { + id: number; + isDraft: boolean; + textEn: string; + textDe?: string; + textFr?: string; + textIt?: string; + textRo?: string; + creation: Date; + expiration?: Date; +} + +export interface TermsAccepted { + userId: number; + user: User; + termId: number; + term: Term; + acceptedAt: Date; +} + +export interface User { + id: number; + subjectId: string; + name: string; + firstName: string; + lastName: string; + isAdmin: boolean; + isDisabled?: boolean; + disabledAt?: Date | string; + createdAt?: Date | string; + settings?: string; + workgroupRoles?: WorkgroupRole[]; + acceptedTerms?: TermsAccepted[]; +} diff --git a/src/client/src/api/fetchApiV2.js b/src/client/src/api/fetchApiV2.js index 6ae1fd37e..d6e858681 100644 --- a/src/client/src/api/fetchApiV2.js +++ b/src/client/src/api/fetchApiV2.js @@ -120,8 +120,6 @@ export const deleteFaciesDescription = async id => { return await fetchApiV2(`faciesdescription?id=${id}`, "DELETE"); }; -export const fetchUsers = async () => await fetchApiV2("user", "GET"); - // stratigraphy export const fetchStratigraphy = async id => { return await fetchApiV2(`stratigraphy/${id}`, "GET"); diff --git a/src/client/src/api/user.ts b/src/client/src/api/user.ts new file mode 100644 index 000000000..bb3d6f9fc --- /dev/null +++ b/src/client/src/api/user.ts @@ -0,0 +1,21 @@ +import { fetchApiV2 } from "./fetchApiV2"; +import { User } from "./apiInterfaces.ts"; + +export const fetchCurrentUser = async () => await fetchApiV2("user/self", "GET"); + +export const fetchUser = async (id: number) => await fetchApiV2(`user/${id}`, "GET"); + +export const fetchUsers = async () => await fetchApiV2("user", "GET"); + +export const updateUser = async (user: User) => { + if (user.disabledAt) { + user.disabledAt = new Date(user.disabledAt).toISOString(); + } + user.isDisabled = undefined; + user.acceptedTerms = undefined; + user.workgroupRoles = undefined; + + return await fetchApiV2("user", "PUT", user); +}; + +export const deleteUser = async (id: number) => await fetchApiV2(`user/${id}`, "DELETE"); diff --git a/src/client/src/pages/settings/admin/adminSettings.jsx b/src/client/src/pages/settings/admin/adminSettings.jsx index 8f6e70582..798249139 100644 --- a/src/client/src/pages/settings/admin/adminSettings.jsx +++ b/src/client/src/pages/settings/admin/adminSettings.jsx @@ -3,22 +3,17 @@ import { connect } from "react-redux"; import PropTypes from "prop-types"; import { withTranslation } from "react-i18next"; import { AlertContext } from "../../../components/alert/alertContext"; -import { fetchUsers } from "../../../api/fetchApiV2"; +import { deleteUser, fetchUser, fetchUsers, updateUser } from "../../../api/user"; import { Button, Checkbox, Form, Icon, Input, Label, Loader, Modal, Table } from "semantic-ui-react"; import { createWorkgroup, - deleteUser, deleteWorkgroup, - disableUser, disableWorkgroup, - enableUser, enableWorkgroup, listWorkgroups, - reloadUser, setRole, - updateUser, updateWorkgroup, } from "../../../api-lib/index"; @@ -242,12 +237,10 @@ class AdminSettings extends React.Component { label=" " disabled={this.state.uId == null} onClick={() => { - updateUser(this.state.uId, this.state.uAdmin).then(response => { - if (response.data.success === false) { - this.context.showAlert(response.data.message, "error"); - } else { - this.listUsers(); - } + const user = this.state.user; + user.isAdmin = this.state.uAdmin; + updateUser(user).then(() => { + this.listUsers(); }); }}> @@ -425,11 +418,13 @@ class AdminSettings extends React.Component {
- {this.state.deleteUser === null ? null : this.state.deleteUser.disabledAt !== null ? ( + {this.state.deleteUser === null ? null : this.state.deleteUser.isDisabled ? (