feat: Add _getMap and _postMap methods for improved API handling and refactor resetPassword to use _postMap
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-11 10:40:54 +02:00
parent c63afad672
commit 573c12cdc3
@@ -42,7 +42,7 @@ class AdminRepository {
return _parseList(data, fromJson); return _parseList(data, fromJson);
} }
/// POST-anrop som returnerar ett typad objekt med [fromJson]. /// POST-anrop som returnerar ett typat objekt med [fromJson].
Future<T> _post<T>( Future<T> _post<T>(
String path, { String path, {
required Map<String, dynamic>? body, required Map<String, dynamic>? body,
@@ -57,7 +57,7 @@ class AdminRepository {
return parse(data); return parse(data);
} }
/// PATCH-anrop som returnerar ett typad objekt med [fromJson]. /// PATCH-anrop som returnerar ett typat objekt med [fromJson].
Future<T> _patch<T>( Future<T> _patch<T>(
String path, { String path, {
required Map<String, dynamic> body, required Map<String, dynamic> body,
@@ -98,6 +98,31 @@ class AdminRepository {
); );
} }
Future<Map<String, dynamic>> _getMap(
String path, {
bool requiresAuth = true,
}) async {
final token = requiresAuth ? await _token() : null;
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(path, token: token),
);
return Map<String, dynamic>.from(data as Map);
}
Future<Map<String, dynamic>> _postMap(
String path, {
Map<String, dynamic>? body,
bool requiresAuth = true,
}) async {
final token = requiresAuth ? await _token() : null;
final data = await guardedApiCall(
_ref,
() => _apiClient.postJson(path, body: body, token: token),
);
return Map<String, dynamic>.from(data as Map);
}
/// Tolerant listparsning — accepterar ren lista eller wrapper ({items, data}). /// Tolerant listparsning — accepterar ren lista eller wrapper ({items, data}).
static List<T> _parseList<T>( static List<T> _parseList<T>(
dynamic data, dynamic data,
@@ -164,17 +189,16 @@ class AdminRepository {
/// Returns `{ temporaryPassword, to, subject, body }`. /// Returns `{ temporaryPassword, to, subject, body }`.
Future<Map<String, dynamic>> resetPassword(int userId) => Future<Map<String, dynamic>> resetPassword(int userId) =>
_post<Map<String, dynamic>>( _postMap(UserApiPaths.resetPassword(userId), body: null);
UserApiPaths.resetPassword(userId),
body: null,
parse: (d) => d as Map<String, dynamic>,
);
// ── Produkter ────────────────────────────────────────────────────────────── // ── Produkter ──────────────────────────────────────────────────────────────
Future<List<AdminProduct>> listProducts() => Future<List<AdminProduct>> listProducts() =>
_getList(ProductApiPaths.list, AdminProduct.fromJson, requiresAuth: false); _getList(ProductApiPaths.list, AdminProduct.fromJson, requiresAuth: false);
@Deprecated('Use listProducts(). Kept for temporary compatibility.')
Future<List<AdminProduct>> listGlobalProducts() => listProducts();
Future<List<PendingProduct>> listPrivateProducts() => Future<List<PendingProduct>> listPrivateProducts() =>
_getList(ProductApiPaths.privateList, PendingProduct.fromJson); _getList(ProductApiPaths.privateList, PendingProduct.fromJson);
@@ -204,8 +228,6 @@ class AdminRepository {
_postVoid(ProductApiPaths.restore(productId)); _postVoid(ProductApiPaths.restore(productId));
// ── Product canonical name updates ──────────────────────────────────────── // ── Product canonical name updates ────────────────────────────────────────
// Admin can update any product; users can only update their own private products
Future<void> updateCanonicalName(int productId, String canonicalName) => Future<void> updateCanonicalName(int productId, String canonicalName) =>
_patchVoid( _patchVoid(
ProductApiPaths.canonicalName(productId), ProductApiPaths.canonicalName(productId),
@@ -219,8 +241,6 @@ class AdminRepository {
); );
// ── Product merging ──────────────────────────────────────────────────────── // ── Product merging ────────────────────────────────────────────────────────
// Admin can merge any products; users can only merge their own private products
Future<void> mergeProductsPrivate({ Future<void> mergeProductsPrivate({
required int sourceProductId, required int sourceProductId,
required int targetProductId, required int targetProductId,
@@ -232,23 +252,28 @@ class AdminRepository {
/// Skapar en ny aktiv produkt (kräver admin). Returnerar `{id, name, categoryId?}`. /// Skapar en ny aktiv produkt (kräver admin). Returnerar `{id, name, categoryId?}`.
Future<Map<String, dynamic>> createProduct(String name, {int? categoryId}) => Future<Map<String, dynamic>> createProduct(String name, {int? categoryId}) =>
_post<Map<String, dynamic>>( _postMap(
ProductApiPaths.list, ProductApiPaths.list,
body: { body: {
'name': name.trim(), 'name': name.trim(),
if (categoryId != null) 'categoryId': categoryId, if (categoryId != null) 'categoryId': categoryId,
}, },
parse: (d) => d as Map<String, dynamic>,
); );
int _parseUpdatedCount(dynamic data) {
if (data is! Map) {
debugPrint('[AdminRepository] bulkSetCategory unexpected response type: ${data.runtimeType}');
return 0;
}
final map = Map<String, dynamic>.from(data);
return (map['updated'] as num?)?.toInt() ?? 0;
}
Future<int> bulkSetCategory(List<int> ids, {required int? categoryId}) => Future<int> bulkSetCategory(List<int> ids, {required int? categoryId}) =>
_post<int>( _post<int>(
ProductApiPaths.bulkUpdate, ProductApiPaths.bulkUpdate,
body: {'ids': ids, 'categoryId': categoryId}, body: {'ids': ids, 'categoryId': categoryId},
parse: (d) { parse: _parseUpdatedCount,
final map = Map<String, dynamic>.from(d as Map);
return (map['updated'] as num?)?.toInt() ?? 0;
},
); );
Future<void> mergeProducts({ Future<void> mergeProducts({
@@ -263,17 +288,8 @@ class AdminRepository {
Future<Map<String, dynamic>> previewMerge({ Future<Map<String, dynamic>> previewMerge({
required int sourceProductId, required int sourceProductId,
required int targetProductId, required int targetProductId,
}) async { }) =>
final token = await _token(); _getMap(ProductApiPaths.mergePreview(sourceProductId, targetProductId));
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(
ProductApiPaths.mergePreview(sourceProductId, targetProductId),
token: token,
),
);
return Map<String, dynamic>.from(data as Map);
}
Future<List<AdminAiCategorizeResult>> aiCategorizeBulk({ Future<List<AdminAiCategorizeResult>> aiCategorizeBulk({
List<int>? productIds, List<int>? productIds,
@@ -438,15 +454,8 @@ class AdminRepository {
Future<Map<String, dynamic>> previewAdminInventoryMerge({ Future<Map<String, dynamic>> previewAdminInventoryMerge({
required int sourceInventoryId, required int sourceInventoryId,
required int targetInventoryId, required int targetInventoryId,
}) async { }) =>
final token = await _token(); _getMap(
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(
AdminInventoryApiPaths.mergePreview(sourceInventoryId, targetInventoryId), AdminInventoryApiPaths.mergePreview(sourceInventoryId, targetInventoryId),
token: token,
),
); );
return Map<String, dynamic>.from(data as Map);
}
} }