Untis-API von Server zu Client
This commit is contained in:
@@ -244,177 +244,6 @@ public static class Endpoints
|
||||
});
|
||||
}
|
||||
|
||||
// ── WebUntis (granulare Abrufe über benutzergebundene RAM-Sitzungen) ──────
|
||||
|
||||
public static void MapWebUntisEndpoints(this WebApplication app)
|
||||
{
|
||||
var group = app.MapGroup("/api/webuntis").RequireAuthorization();
|
||||
|
||||
group.MapGet("/connection", (ClaimsPrincipal user, WebUntisConnectionStore connections) =>
|
||||
{
|
||||
var uid = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
return uid is null ? Results.Unauthorized() : Results.Ok(connections.GetStatus(uid));
|
||||
});
|
||||
|
||||
group.MapPost("/connection", async (WebUntisConnectRequest request, ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var uid = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (uid is null) return Results.Unauthorized();
|
||||
if (string.IsNullOrWhiteSpace(request.School) || string.IsNullOrWhiteSpace(request.Username) ||
|
||||
string.IsNullOrWhiteSpace(request.Password))
|
||||
return Results.BadRequest("Schule, Benutzername und Passwort sind erforderlich.");
|
||||
return await WebUntisResult(() => connections.ConnectAsync(uid, request, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapDelete("/connection", async (ClaimsPrincipal user, WebUntisConnectionStore connections) =>
|
||||
{
|
||||
var uid = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (uid is null) return Results.Unauthorized();
|
||||
await connections.DisconnectAsync(uid);
|
||||
return Results.NoContent();
|
||||
});
|
||||
|
||||
group.MapGet("/schoolyears", (ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetSchoolYearsAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/classes", ([FromQuery] int schoolyearId, ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback,
|
||||
CancellationToken cancellationToken) => schoolyearId <= 0
|
||||
? Task.FromResult<IResult>(Results.BadRequest("schoolyearId muss größer als 0 sein."))
|
||||
: WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetClassesAsync(schoolyearId, cancellationToken)));
|
||||
|
||||
group.MapGet("/teachers", (ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback, client => client.GetTeachersAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/student-report", ([FromQuery] string? className, ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetStudentReportAsync(className, cancellationToken)));
|
||||
|
||||
group.MapGet("/holidays", (ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback, client => client.GetHolidaysAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/timegrid", (ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback, client => client.GetTimeGridAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/substitutions", ([FromQuery] int startDate, [FromQuery] int endDate,
|
||||
[FromQuery] int? departmentId, ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!ValidDateRange(startDate, endDate, 31, out var error))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(error));
|
||||
return WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetSubstitutionsAsync(startDate, endDate, departmentId, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/timetable", ([FromQuery] string elementType, [FromQuery] int elementId,
|
||||
[FromQuery] int startDate, [FromQuery] int endDate, ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Enum.TryParse<UntisTimetableElementType>(elementType, true, out var parsedType) ||
|
||||
!Enum.IsDefined(parsedType))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(
|
||||
"elementType muss class, teacher, subject, room oder student sein."));
|
||||
if (elementId <= 0)
|
||||
return Task.FromResult<IResult>(Results.BadRequest("elementId muss größer als 0 sein."));
|
||||
if (!ValidDateRange(startDate, endDate, 62, out var error))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(error));
|
||||
return WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetTimetableAsync(parsedType, elementId, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/students/{studentKey:int}/absences", (int studentKey, [FromQuery] int startDate,
|
||||
[FromQuery] int endDate, ClaimsPrincipal user, WebUntisConnectionStore connections,
|
||||
WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (studentKey <= 0)
|
||||
return Task.FromResult<IResult>(Results.BadRequest("studentKey muss größer als 0 sein."));
|
||||
if (!ValidDateRange(startDate, endDate, 400, out var error))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(error));
|
||||
return WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetStudentAbsencesAsync(studentKey, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/students/{studentId:int}/class-register-entries", (int studentId,
|
||||
[FromQuery] int startDate, [FromQuery] int endDate, ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (studentId <= 0)
|
||||
return Task.FromResult<IResult>(Results.BadRequest("studentId muss größer als 0 sein."));
|
||||
if (!ValidDateRange(startDate, endDate, 400, out var error))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(error));
|
||||
return WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetClassRegisterEntriesAsync(studentId, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/class-register/categories", (ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetClassRegisterCategoriesAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/class-register/category-groups", (ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, CancellationToken cancellationToken) =>
|
||||
WithWebUntisClient(user, connections, fallback,
|
||||
client => client.GetClassRegisterCategoryGroupsAsync(cancellationToken)));
|
||||
}
|
||||
|
||||
private static Task<IResult> WithWebUntisClient<T>(ClaimsPrincipal user,
|
||||
WebUntisConnectionStore connections, WebUntisClient fallback, Func<WebUntisClient, Task<T>> operation)
|
||||
{
|
||||
var uid = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (uid is null) return Task.FromResult<IResult>(Results.Unauthorized());
|
||||
return WebUntisResult(() => operation(connections.GetClient(uid) ?? fallback));
|
||||
}
|
||||
|
||||
private static async Task<IResult> WebUntisResult<T>(Func<Task<T>> operation)
|
||||
{
|
||||
try { return Results.Ok(await operation()); }
|
||||
catch (WebUntisConfigurationException exception)
|
||||
{
|
||||
return Results.Problem(exception.Message, statusCode: StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
catch (WebUntisException exception)
|
||||
{
|
||||
return Results.Problem(exception.Message, statusCode: StatusCodes.Status502BadGateway);
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
return Results.Problem($"Der WebUntis-Report ist ungültig: {exception.Message}",
|
||||
statusCode: StatusCodes.Status502BadGateway);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValidDateRange(int startDate, int endDate, int maximumDays, out string error)
|
||||
{
|
||||
error = "";
|
||||
if (!DateOnly.TryParseExact(startDate.ToString(CultureInfo.InvariantCulture), "yyyyMMdd",
|
||||
CultureInfo.InvariantCulture, DateTimeStyles.None, out var start) ||
|
||||
!DateOnly.TryParseExact(endDate.ToString(CultureInfo.InvariantCulture), "yyyyMMdd",
|
||||
CultureInfo.InvariantCulture, DateTimeStyles.None, out var end))
|
||||
{
|
||||
error = "startDate und endDate müssen gültige Datumswerte im Format yyyyMMdd sein.";
|
||||
return false;
|
||||
}
|
||||
if (end < start)
|
||||
{
|
||||
error = "endDate darf nicht vor startDate liegen.";
|
||||
return false;
|
||||
}
|
||||
if (end.DayNumber - start.DayNumber + 1 > maximumDays)
|
||||
{
|
||||
error = $"Der Zeitraum darf höchstens {maximumDays} Tage umfassen.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string[]> ValidateLocation(SchoolLocationRequest request)
|
||||
{
|
||||
var errors = new Dictionary<string, string[]>();
|
||||
|
||||
Reference in New Issue
Block a user