Frontend gaps fill work. Testing fixes work. Auditing in progress.
This commit is contained in:
@@ -29,8 +29,21 @@ internal static class OfflineKitEndpoints
|
||||
.MapGroup("/api/offline-kit")
|
||||
.WithTags("Offline Kit");
|
||||
|
||||
// Sprint 026: OFFLINE-012 - Legacy v1 alias for backward compatibility
|
||||
var v1Group = endpoints
|
||||
.MapGroup("/api/v1/offline-kit")
|
||||
.WithTags("Offline Kit");
|
||||
|
||||
MapEndpointsToGroup(group, isLegacy: false);
|
||||
MapEndpointsToGroup(v1Group, isLegacy: true);
|
||||
}
|
||||
|
||||
private static void MapEndpointsToGroup(RouteGroupBuilder group, bool isLegacy)
|
||||
{
|
||||
var suffix = isLegacy ? ".v1" : "";
|
||||
|
||||
group.MapPost("/import", HandleImportAsync)
|
||||
.WithName("scanner.offline-kit.import")
|
||||
.WithName($"scanner.offline-kit.import{suffix}")
|
||||
.RequireAuthorization(ScannerPolicies.OfflineKitImport)
|
||||
.Produces<OfflineKitImportResponseTransport>(StatusCodes.Status202Accepted)
|
||||
.Produces<ProblemDetails>(StatusCodes.Status400BadRequest)
|
||||
@@ -38,11 +51,26 @@ internal static class OfflineKitEndpoints
|
||||
.Produces<ProblemDetails>(StatusCodes.Status422UnprocessableEntity);
|
||||
|
||||
group.MapGet("/status", HandleStatusAsync)
|
||||
.WithName("scanner.offline-kit.status")
|
||||
.WithName($"scanner.offline-kit.status{suffix}")
|
||||
.RequireAuthorization(ScannerPolicies.OfflineKitStatusRead)
|
||||
.Produces<OfflineKitStatusTransport>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);
|
||||
|
||||
// Sprint 026: OFFLINE-011 - Manifest retrieval
|
||||
group.MapGet("/manifest", HandleGetManifestAsync)
|
||||
.WithName($"scanner.offline-kit.manifest{suffix}")
|
||||
.RequireAuthorization(ScannerPolicies.OfflineKitManifestRead)
|
||||
.Produces<OfflineKitManifestTransport>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces<ProblemDetails>(StatusCodes.Status404NotFound);
|
||||
|
||||
// Sprint 026: OFFLINE-011 - Bundle validation
|
||||
group.MapPost("/validate", HandleValidateAsync)
|
||||
.WithName($"scanner.offline-kit.validate{suffix}")
|
||||
.RequireAuthorization(ScannerPolicies.OfflineKitValidate)
|
||||
.Produces<OfflineKitValidationResult>(StatusCodes.Status200OK)
|
||||
.Produces<ProblemDetails>(StatusCodes.Status400BadRequest);
|
||||
}
|
||||
|
||||
private static async Task<IResult> HandleImportAsync(
|
||||
@@ -226,5 +254,88 @@ internal static class OfflineKitEndpoints
|
||||
|
||||
return "anonymous";
|
||||
}
|
||||
|
||||
// Sprint 026: OFFLINE-011 - Manifest retrieval handler
|
||||
private static async Task<IResult> HandleGetManifestAsync(
|
||||
HttpContext context,
|
||||
IOptionsMonitor<OfflineKitOptions> offlineKitOptions,
|
||||
OfflineKitManifestService manifestService,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
ArgumentNullException.ThrowIfNull(offlineKitOptions);
|
||||
ArgumentNullException.ThrowIfNull(manifestService);
|
||||
|
||||
if (!offlineKitOptions.CurrentValue.Enabled)
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.NotFound,
|
||||
"Offline kit is not enabled",
|
||||
StatusCodes.Status404NotFound);
|
||||
}
|
||||
|
||||
var tenantId = ResolveTenant(context);
|
||||
var manifest = await manifestService.GetManifestAsync(tenantId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return manifest is null
|
||||
? Results.NoContent()
|
||||
: Results.Ok(manifest);
|
||||
}
|
||||
|
||||
// Sprint 026: OFFLINE-011 - Bundle validation handler
|
||||
private static async Task<IResult> HandleValidateAsync(
|
||||
HttpContext context,
|
||||
HttpRequest request,
|
||||
IOptionsMonitor<OfflineKitOptions> offlineKitOptions,
|
||||
OfflineKitManifestService manifestService,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
ArgumentNullException.ThrowIfNull(offlineKitOptions);
|
||||
ArgumentNullException.ThrowIfNull(manifestService);
|
||||
|
||||
if (!offlineKitOptions.CurrentValue.Enabled)
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.NotFound,
|
||||
"Offline kit validation is not enabled",
|
||||
StatusCodes.Status404NotFound);
|
||||
}
|
||||
|
||||
OfflineKitValidationRequest? validationRequest;
|
||||
try
|
||||
{
|
||||
validationRequest = await request.ReadFromJsonAsync<OfflineKitValidationRequest>(JsonOptions, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.Validation,
|
||||
"Invalid validation request",
|
||||
StatusCodes.Status400BadRequest,
|
||||
detail: $"Failed to parse request JSON: {ex.Message}");
|
||||
}
|
||||
|
||||
if (validationRequest is null || string.IsNullOrWhiteSpace(validationRequest.ManifestJson))
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.Validation,
|
||||
"Invalid validation request",
|
||||
StatusCodes.Status400BadRequest,
|
||||
detail: "Request body with manifestJson is required.");
|
||||
}
|
||||
|
||||
var result = manifestService.ValidateManifest(
|
||||
validationRequest.ManifestJson,
|
||||
validationRequest.Signature,
|
||||
validationRequest.VerifyAssets);
|
||||
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user