Vorarbeit: WebUntis-API
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Sync.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -243,6 +244,132 @@ public static class Endpoints
|
||||
});
|
||||
}
|
||||
|
||||
// ── WebUntis (granulare, zustandslose Abrufe) ─────────────────────────────
|
||||
|
||||
public static void MapWebUntisEndpoints(this WebApplication app)
|
||||
{
|
||||
var group = app.MapGroup("/api/webuntis").RequireAuthorization();
|
||||
|
||||
group.MapGet("/schoolyears", (WebUntisClient client, CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetSchoolYearsAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/classes", ([FromQuery] int schoolyearId, WebUntisClient client,
|
||||
CancellationToken cancellationToken) => schoolyearId <= 0
|
||||
? Task.FromResult<IResult>(Results.BadRequest("schoolyearId muss größer als 0 sein."))
|
||||
: WebUntisResult(() => client.GetClassesAsync(schoolyearId, cancellationToken)));
|
||||
|
||||
group.MapGet("/teachers", (WebUntisClient client, CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetTeachersAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/student-report", ([FromQuery] string? className, WebUntisClient client,
|
||||
CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetStudentReportAsync(className, cancellationToken)));
|
||||
|
||||
group.MapGet("/holidays", (WebUntisClient client, CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetHolidaysAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/timegrid", (WebUntisClient client, CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetTimeGridAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/substitutions", ([FromQuery] int startDate, [FromQuery] int endDate,
|
||||
[FromQuery] int? departmentId, WebUntisClient client, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!ValidDateRange(startDate, endDate, 31, out var error))
|
||||
return Task.FromResult<IResult>(Results.BadRequest(error));
|
||||
return WebUntisResult(() =>
|
||||
client.GetSubstitutionsAsync(startDate, endDate, departmentId, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/timetable", ([FromQuery] string elementType, [FromQuery] int elementId,
|
||||
[FromQuery] int startDate, [FromQuery] int endDate, WebUntisClient client,
|
||||
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 WebUntisResult(() =>
|
||||
client.GetTimetableAsync(parsedType, elementId, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/students/{studentKey:int}/absences", (int studentKey, [FromQuery] int startDate,
|
||||
[FromQuery] int endDate, WebUntisClient client, 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 WebUntisResult(() =>
|
||||
client.GetStudentAbsencesAsync(studentKey, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/students/{studentId:int}/class-register-entries", (int studentId,
|
||||
[FromQuery] int startDate, [FromQuery] int endDate, WebUntisClient client,
|
||||
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 WebUntisResult(() =>
|
||||
client.GetClassRegisterEntriesAsync(studentId, startDate, endDate, cancellationToken));
|
||||
});
|
||||
|
||||
group.MapGet("/class-register/categories", (WebUntisClient client,
|
||||
CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetClassRegisterCategoriesAsync(cancellationToken)));
|
||||
|
||||
group.MapGet("/class-register/category-groups", (WebUntisClient client,
|
||||
CancellationToken cancellationToken) =>
|
||||
WebUntisResult(() => client.GetClassRegisterCategoryGroupsAsync(cancellationToken)));
|
||||
}
|
||||
|
||||
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