Skip to content

Commit 2ec0e73

Browse files
fix user bulk create
1 parent 450f140 commit 2ec0e73

5 files changed

Lines changed: 181 additions & 105 deletions

File tree

school_data_hub_client/lib/src/protocol/client.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ class EndpointAdminUser extends _i1.EndpointRef {
401401
);
402402

403403
/// Batch-creates users. Returns credentials for successes and errors for skipped/failed rows.
404+
/// Each create runs in its own transaction; creates are executed in parallel (up to 5 at a time) to reduce timeout risk.
404405
_i2.Future<_i13.BatchCreateUsersResponse> batchCreateUsers(
405406
List<_i14.CreateUserRequest> requests) =>
406407
caller.callServerEndpoint<_i13.BatchCreateUsersResponse>(

school_data_hub_flutter/lib/features/matrix/users/presentation/matrix_users_list_page/matrix_users_list_page.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class _MatrixUsersListContent extends WatchingWidget {
194194
static Future<Map<String?, UserWithDevices>> _loadAppUsersByMatrixId() async {
195195
try {
196196
final list = await di<UserApiService>().getAllUsersWithDevices();
197+
if (list == null) return {};
197198
final map = <String?, UserWithDevices>{};
198199
for (final u in list) {
199200
final matrixId = u.user.matrixUserId;
Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
import 'package:flutter_it/flutter_it.dart';
22
import 'package:school_data_hub_client/school_data_hub_client.dart';
3+
import 'package:school_data_hub_flutter/core/client/client_helper.dart';
34

45
/// API service for user-related operations.
56
class UserApiService {
67
Client get _client => di<Client>();
78

89
/// Fetch all users.
9-
Future<List<User>> getAllUsers() async {
10-
return _client.user.getAllUsers();
10+
Future<List<User>?> getAllUsers() async {
11+
return ClientHelper.apiCall(
12+
call: () => _client.user.getAllUsers(),
13+
errorMessage: 'Benutzer abrufen',
14+
);
1115
}
1216

1317
/// Fetch all users with UserInfo and list of UserDevices per user.
14-
Future<List<UserWithDevices>> getAllUsersWithDevices() async {
15-
return _client.user.getAllUsersWithDevices();
18+
Future<List<UserWithDevices>?> getAllUsersWithDevices() async {
19+
return ClientHelper.apiCall(
20+
call: () => _client.user.getAllUsersWithDevices(),
21+
errorMessage: 'Benutzer mit Geräten abrufen',
22+
);
1623
}
1724

1825
/// Get the currently authenticated user.
1926
Future<User?> getCurrentUser() async {
20-
return _client.user.getCurrentUser();
27+
return ClientHelper.apiCall<User?>(
28+
call: () => _client.user.getCurrentUser(),
29+
errorMessage: 'Aktuellen Benutzer abrufen',
30+
);
2131
}
2232

2333
/// Create a new user.
@@ -34,18 +44,21 @@ class UserApiService {
3444
required bool isTester,
3545
Set<int>? pupilsAuth,
3646
}) async {
37-
await _client.adminUser.createUser(
38-
userName: userName,
39-
fullName: fullName,
40-
email: email,
41-
matrixUserId: matrixUserId,
42-
password: password,
43-
role: role,
44-
timeUnits: timeUnits,
45-
reliefTimeUnits: reliefTimeUnits,
46-
scopeNames: scopeNames,
47-
isTester: isTester,
48-
pupilsAuth: pupilsAuth,
47+
await ClientHelper.apiCall(
48+
call: () => _client.adminUser.createUser(
49+
userName: userName,
50+
fullName: fullName,
51+
email: email,
52+
matrixUserId: matrixUserId,
53+
password: password,
54+
role: role,
55+
timeUnits: timeUnits,
56+
reliefTimeUnits: reliefTimeUnits,
57+
scopeNames: scopeNames,
58+
isTester: isTester,
59+
pupilsAuth: pupilsAuth,
60+
),
61+
errorMessage: 'Benutzer erstellen',
4962
);
5063
}
5164

@@ -65,55 +78,76 @@ class UserApiService {
6578
String? imageUrl,
6679
Set<int>? pupilsAuth,
6780
}) async {
68-
await _client.adminUser.updateUser(
69-
userId,
70-
userName: userName,
71-
fullName: fullName,
72-
email: email,
73-
role: role,
74-
matrixUserId: matrixUserId,
75-
timeUnits: timeUnits,
76-
reliefTimeUnits: reliefTimeUnits,
77-
credit: credit,
78-
isTester: isTester,
79-
pupilsAuth: pupilsAuth,
81+
await ClientHelper.apiCall(
82+
call: () => _client.adminUser.updateUser(
83+
userId,
84+
userName: userName,
85+
fullName: fullName,
86+
email: email,
87+
role: role,
88+
matrixUserId: matrixUserId,
89+
timeUnits: timeUnits,
90+
reliefTimeUnits: reliefTimeUnits,
91+
credit: credit,
92+
isTester: isTester,
93+
pupilsAuth: pupilsAuth,
94+
),
95+
errorMessage: 'Benutzer aktualisieren',
8096
);
8197
// TODO: when server supports UserInfo.imageUrl, add imageUrl to the
8298
// endpoint and pass it here (client must be regenerated).
8399
}
84100

85101
/// Reset a user's password. Returns `true` on success.
86-
Future<bool> resetPassword(String userEmail, String newPassword) async {
87-
return _client.adminUser.resetPassword(userEmail, newPassword);
102+
Future<bool?> resetPassword(String userEmail, String newPassword) async {
103+
return ClientHelper.apiCall(
104+
call: () => _client.adminUser.resetPassword(userEmail, newPassword),
105+
errorMessage: 'Passwort zurücksetzen',
106+
);
88107
}
89108

90109
/// Change the current user's password. Returns `true` on success.
91-
Future<bool> changePassword(String oldPassword, String newPassword) async {
92-
return _client.user.changePassword(oldPassword, newPassword);
110+
Future<bool?> changePassword(String oldPassword, String newPassword) async {
111+
return ClientHelper.apiCall(
112+
call: () => _client.user.changePassword(oldPassword, newPassword),
113+
errorMessage: 'Passwort ändern',
114+
);
93115
}
94116

95117
/// Delete (block) a user by ID.
96118
Future<void> deleteUser(int userId) async {
97-
await _client.adminUser.deleteUser(userId);
119+
await ClientHelper.apiCall(
120+
call: () => _client.adminUser.deleteUser(userId),
121+
errorMessage: 'Benutzer löschen',
122+
);
98123
}
99124

100125
/// Delete the auth key associated with a device. Returns updated user+devices
101126
/// for the session user (or null if not authenticated).
102127
Future<UserWithDevices?> deleteAuthKeyAssociatedWithDevice(
103128
UserDevice device,
104129
) async {
105-
return _client.adminUser.deleteAuthKeyAssociatedWithDevice(device);
130+
return ClientHelper.apiCall<UserWithDevices?>(
131+
call: () => _client.adminUser.deleteAuthKeyAssociatedWithDevice(device),
132+
errorMessage: 'Geräte-Auth-Key löschen',
133+
);
106134
}
107135

108136
/// Increase staff credit for all users. Returns `true` on success.
109-
Future<bool> increaseStaffCredit() async {
110-
return _client.user.increaseStaffCredit();
137+
Future<bool?> increaseStaffCredit() async {
138+
return ClientHelper.apiCall(
139+
call: () => _client.user.increaseStaffCredit(),
140+
errorMessage: 'Mitarbeiter-Guthaben erhöhen',
141+
);
111142
}
112143

113144
/// Batch-creates users. Returns credentials for successes and errors for skipped/failed rows.
114-
Future<BatchCreateUsersResponse> batchCreateUsers(
145+
Future<BatchCreateUsersResponse?> batchCreateUsers(
115146
List<CreateUserRequest> requests,
116147
) async {
117-
return _client.adminUser.batchCreateUsers(requests);
148+
return ClientHelper.apiCall(
149+
call: () => _client.adminUser.batchCreateUsers(requests),
150+
errorMessage: 'Benutzer-Stapelimport',
151+
);
118152
}
119153
}

school_data_hub_flutter/lib/features/user/domain/user_manager.dart

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ class UserManager {
126126
//-- Command implementations --
127127

128128
Future<void> _fetchUsers() async {
129-
final List<UserWithDevices> list = await _apiService
130-
.getAllUsersWithDevices();
129+
final list = await _apiService.getAllUsersWithDevices();
130+
if (list == null) return;
131131
list.sort(
132132
(a, b) => (a.user.userInfo?.userName ?? '').compareTo(
133133
b.user.userInfo?.userName ?? '',
@@ -152,7 +152,7 @@ class UserManager {
152152
pupilsAuth: params.pupilsAuth,
153153
);
154154
final userWithDetails = await _apiService.getCurrentUser();
155-
_addUser(userWithDetails!);
155+
if (userWithDetails != null) _addUser(userWithDetails);
156156

157157
_notificationService.showSnackBar(
158158
NotificationType.success,
@@ -165,7 +165,7 @@ class UserManager {
165165
params.userEmail,
166166
params.newPassword,
167167
);
168-
if (!success) {
168+
if (success != true) {
169169
throw Exception('Passwort konnte nicht zurückgesetzt werden!');
170170
}
171171
_notificationService.showSnackBar(
@@ -179,7 +179,7 @@ class UserManager {
179179
params.oldPassword,
180180
params.newPassword,
181181
);
182-
if (!success) {
182+
if (success != true) {
183183
throw Exception('Passwort konnte nicht geändert werden!');
184184
}
185185
_notificationService.showSnackBar(
@@ -233,7 +233,7 @@ class UserManager {
233233

234234
Future<void> _increaseUsersCredit() async {
235235
final success = await _apiService.increaseStaffCredit();
236-
if (!success) {
236+
if (success != true) {
237237
throw Exception('Guthaben konnte nicht erhöht werden!');
238238
}
239239
_notificationService.showSnackBar(
@@ -255,24 +255,28 @@ class UserManager {
255255
final email = row.email.trim().isEmpty
256256
? '$userName@schule.local'
257257
: row.email.trim();
258-
final scopeNames =
259-
row.role == Role.admin ? ['admin'] : <String>[];
260-
requests.add(CreateUserRequest(
261-
userName: userName,
262-
fullName: row.fullName,
263-
email: email,
264-
password: gen(row),
265-
role: row.role,
266-
timeUnits: row.timeUnits,
267-
reliefTimeUnits: row.reliefTimeUnits,
268-
scopeNames: scopeNames,
269-
isTester: false,
270-
matrixUserId: null,
271-
credit: null,
272-
pupilsAuth: null,
273-
));
258+
final scopeNames = row.role == Role.admin ? ['admin'] : <String>[];
259+
requests.add(
260+
CreateUserRequest(
261+
userName: userName,
262+
fullName: row.fullName,
263+
email: email,
264+
password: gen(row),
265+
role: row.role,
266+
timeUnits: row.timeUnits,
267+
reliefTimeUnits: row.reliefTimeUnits,
268+
scopeNames: scopeNames,
269+
isTester: false,
270+
matrixUserId: null,
271+
credit: null,
272+
pupilsAuth: null,
273+
),
274+
);
274275
}
275276
final response = await _apiService.batchCreateUsers(requests);
277+
if (response == null) {
278+
throw Exception('Benutzer-Stapelimport fehlgeschlagen.');
279+
}
276280
final credentials = response.credentials
277281
.map(
278282
(c) => StaffCredentialEntry(

0 commit comments

Comments
 (0)