add MarianumConnectQuery base and adopt it in list queries

This commit is contained in:
2026-07-12 23:24:21 +02:00
parent 9b5198c6db
commit 0a2ff5c3fb
10 changed files with 133 additions and 201 deletions
@@ -0,0 +1,29 @@
import 'package:dio/dio.dart';
import 'errors/marianumconnect_error.dart';
import 'marianumconnect_api.dart';
import 'marianumconnect_endpoint.dart';
/// Shared base for MarianumConnect API queries. Owns the [dio] client (the
/// shared authenticated singleton by default) and routes calls through [guard]
/// so every query maps a DioException to the app's typed AppExceptions the same
/// way instead of repeating the try/catch. Subclasses with bespoke error or
/// lifecycle handling (own dio, silent failure, custom status mapping) may skip
/// [guard] and still reuse [dio]/[endpoint].
abstract class MarianumConnectQuery {
final Dio dio;
MarianumConnectQuery({Dio? dio}) : dio = dio ?? MarianumConnectApi.dio();
/// Resolves [path] against the active mobile-API base URL.
String endpoint(String path) => MarianumConnectEndpoint.resolve(path);
/// Runs [body], converting any DioException into the matching AppException.
Future<T> guard<T>(Future<T> Function() body) async {
try {
return await body();
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
}
@@ -1,25 +1,13 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import '../../models/mc_holiday.dart'; import '../../models/mc_holiday.dart';
class GetHolidays { class GetHolidays extends MarianumConnectQuery {
final Dio _dio; GetHolidays({super.dio});
GetHolidays({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<List<McHoliday>> run() => guard(() async {
final response = await dio.get<List<dynamic>>(endpoint('holidays'));
Future<List<McHoliday>> run() async { return response.data!
try { .map((e) => McHoliday.fromJson(e as Map<String, dynamic>))
final response = await _dio.get<List<dynamic>>( .toList();
MarianumConnectEndpoint.resolve('holidays'), });
);
return response.data!
.map((e) => McHoliday.fromJson(e as Map<String, dynamic>))
.toList();
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_classes_response.dart'; import 'timetable_get_classes_response.dart';
class TimetableGetClasses { class TimetableGetClasses extends MarianumConnectQuery {
final Dio _dio; TimetableGetClasses({super.dio});
TimetableGetClasses({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetClassesResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetClassesResponse> run() async { endpoint('timetable/elements/classes'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/elements/classes'), .map((e) => McTimetableClass.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetClassesResponse(result: list);
.map((e) => McTimetableClass.fromJson(e as Map<String, dynamic>)) });
.toList();
return TimetableGetClassesResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_holidays_response.dart'; import 'timetable_get_holidays_response.dart';
class TimetableGetHolidays { class TimetableGetHolidays extends MarianumConnectQuery {
final Dio _dio; TimetableGetHolidays({super.dio});
TimetableGetHolidays({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetHolidaysResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetHolidaysResponse> run() async { endpoint('timetable/holidays'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/holidays'), .map((e) => McHoliday.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetHolidaysResponse(result: list);
.map((e) => McHoliday.fromJson(e as Map<String, dynamic>)) });
.toList();
return TimetableGetHolidaysResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,14 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_rooms_response.dart'; import 'timetable_get_rooms_response.dart';
class TimetableGetRooms { class TimetableGetRooms extends MarianumConnectQuery {
final Dio _dio; TimetableGetRooms({super.dio});
TimetableGetRooms({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetRoomsResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(endpoint('timetable/rooms'));
Future<TimetableGetRoomsResponse> run() async { final list = response.data!
try { .map((e) => McRoom.fromJson(e as Map<String, dynamic>))
final response = await _dio.get<List<dynamic>>( .toList();
MarianumConnectEndpoint.resolve('timetable/rooms'), return TimetableGetRoomsResponse(result: list);
); });
final list = response.data!
.map((e) => McRoom.fromJson(e as Map<String, dynamic>))
.toList();
return TimetableGetRoomsResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_students_response.dart'; import 'timetable_get_students_response.dart';
class TimetableGetStudents { class TimetableGetStudents extends MarianumConnectQuery {
final Dio _dio; TimetableGetStudents({super.dio});
TimetableGetStudents({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetStudentsResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetStudentsResponse> run() async { endpoint('timetable/elements/students'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/elements/students'), .map((e) => McTimetableStudent.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetStudentsResponse(result: list);
.map((e) => McTimetableStudent.fromJson(e as Map<String, dynamic>)) });
.toList();
return TimetableGetStudentsResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_subjects_response.dart'; import 'timetable_get_subjects_response.dart';
class TimetableGetSubjects { class TimetableGetSubjects extends MarianumConnectQuery {
final Dio _dio; TimetableGetSubjects({super.dio});
TimetableGetSubjects({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetSubjectsResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetSubjectsResponse> run() async { endpoint('timetable/subjects'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/subjects'), .map((e) => McSubject.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetSubjectsResponse(result: list);
.map((e) => McSubject.fromJson(e as Map<String, dynamic>)) });
.toList();
return TimetableGetSubjectsResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,29 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_teachers_response.dart'; import 'timetable_get_teachers_response.dart';
class TimetableGetTeachers { class TimetableGetTeachers extends MarianumConnectQuery {
final Dio _dio; TimetableGetTeachers({super.dio});
TimetableGetTeachers({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetTeachersResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetTeachersResponse> run() async { endpoint('timetable/elements/teachers'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/elements/teachers'), .map((e) => McTimetableTeacherElement.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetTeachersResponse(result: list);
.map( });
(e) =>
McTimetableTeacherElement.fromJson(e as Map<String, dynamic>),
)
.toList();
return TimetableGetTeachersResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,26 +1,16 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'timetable_get_timegrid_response.dart'; import 'timetable_get_timegrid_response.dart';
class TimetableGetTimegrid { class TimetableGetTimegrid extends MarianumConnectQuery {
final Dio _dio; TimetableGetTimegrid({super.dio});
TimetableGetTimegrid({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<TimetableGetTimegridResponse> run() => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<TimetableGetTimegridResponse> run() async { endpoint('timetable/timegrid'),
try { );
final response = await _dio.get<List<dynamic>>( final list = response.data!
MarianumConnectEndpoint.resolve('timetable/timegrid'), .map((e) => McTimegridUnit.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return TimetableGetTimegridResponse(result: list);
.map((e) => McTimegridUnit.fromJson(e as Map<String, dynamic>)) });
.toList();
return TimetableGetTimegridResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }
@@ -1,30 +1,20 @@
import 'package:dio/dio.dart'; import '../../marianumconnect_query.dart';
import '../../errors/marianumconnect_error.dart';
import '../../marianumconnect_api.dart';
import '../../marianumconnect_endpoint.dart';
import 'user_search_response.dart'; import 'user_search_response.dart';
/// Searches active users (students, teachers, staff) via the MarianumConnect /// Searches active users (students, teachers, staff) via the MarianumConnect
/// mobile API. Returns each match's Nextcloud username plus role, so the Talk /// mobile API. Returns each match's Nextcloud username plus role, so the Talk
/// search can start a direct chat and label results without hitting Nextcloud. /// search can start a direct chat and label results without hitting Nextcloud.
class UserSearch { class UserSearch extends MarianumConnectQuery {
final Dio _dio; UserSearch({super.dio});
UserSearch({Dio? dio}) : _dio = dio ?? MarianumConnectApi.dio(); Future<UserSearchResponse> run(String query) => guard(() async {
final response = await dio.get<List<dynamic>>(
Future<UserSearchResponse> run(String query) async { endpoint('users/search'),
try { queryParameters: {'q': query},
final response = await _dio.get<List<dynamic>>( );
MarianumConnectEndpoint.resolve('users/search'), final list = response.data!
queryParameters: {'q': query}, .map((e) => McUserSearchResult.fromJson(e as Map<String, dynamic>))
); .toList();
final list = response.data! return UserSearchResponse(result: list);
.map((e) => McUserSearchResult.fromJson(e as Map<String, dynamic>)) });
.toList();
return UserSearchResponse(result: list);
} on DioException catch (e) {
throw mapMarianumConnectError(e);
}
}
} }