search and ai stabilization work, localization stablized.
This commit is contained in:
@@ -13,6 +13,7 @@ using StellaOps.ExportCenter.WebService.Telemetry;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Api;
|
||||
|
||||
@@ -173,7 +174,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
limit = Math.Clamp(limit == 0 ? 50 : limit, 1, 100);
|
||||
offset = Math.Max(0, offset);
|
||||
@@ -200,7 +201,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var profile = await profileRepo.GetByIdAsync(tenantId, profileId, cancellationToken);
|
||||
if (profile is null)
|
||||
@@ -220,11 +221,11 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
// Validate name uniqueness
|
||||
if (!await profileRepo.IsNameUniqueAsync(tenantId, request.Name, cancellationToken: cancellationToken))
|
||||
return TypedResults.Conflict($"Profile name '{request.Name}' already exists");
|
||||
return TypedResults.Conflict(_t("exportcenter.error.profile_name_conflict", request.Name));
|
||||
|
||||
var now = timeProvider.GetUtcNow();
|
||||
var profile = new ExportProfile
|
||||
@@ -274,21 +275,21 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var existing = await profileRepo.GetByIdAsync(tenantId, profileId, cancellationToken);
|
||||
if (existing is null)
|
||||
return TypedResults.NotFound();
|
||||
|
||||
if (existing.Status == ExportProfileStatus.Archived)
|
||||
return TypedResults.BadRequest("Cannot update archived profile");
|
||||
return TypedResults.BadRequest(_t("exportcenter.error.profile_archived"));
|
||||
|
||||
// Validate name uniqueness if changing
|
||||
if (request.Name is not null &&
|
||||
!request.Name.Equals(existing.Name, StringComparison.OrdinalIgnoreCase) &&
|
||||
!await profileRepo.IsNameUniqueAsync(tenantId, request.Name, profileId, cancellationToken))
|
||||
{
|
||||
return TypedResults.Conflict($"Profile name '{request.Name}' already exists");
|
||||
return TypedResults.Conflict(_t("exportcenter.error.profile_name_conflict", request.Name));
|
||||
}
|
||||
|
||||
var updated = existing with
|
||||
@@ -334,7 +335,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var archived = await profileRepo.ArchiveAsync(tenantId, profileId, cancellationToken);
|
||||
if (!archived)
|
||||
@@ -368,14 +369,14 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var profile = await profileRepo.GetByIdAsync(tenantId, profileId, cancellationToken);
|
||||
if (profile is null)
|
||||
return TypedResults.NotFound();
|
||||
|
||||
if (profile.Status != ExportProfileStatus.Active)
|
||||
return TypedResults.BadRequest("Profile is not active");
|
||||
return TypedResults.BadRequest(_t("exportcenter.error.profile_not_active"));
|
||||
|
||||
var options = concurrencyOptions.Value;
|
||||
|
||||
@@ -472,7 +473,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
limit = Math.Clamp(limit == 0 ? 50 : limit, 1, 100);
|
||||
offset = Math.Max(0, offset);
|
||||
@@ -501,7 +502,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -520,7 +521,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -528,7 +529,7 @@ public static class ExportApiEndpoints
|
||||
|
||||
var cancelled = await runRepo.CancelAsync(tenantId, runId, cancellationToken);
|
||||
if (!cancelled)
|
||||
return TypedResults.BadRequest("Run cannot be cancelled in its current state");
|
||||
return TypedResults.BadRequest(_t("exportcenter.error.run_cancel_invalid_state"));
|
||||
|
||||
await auditService.LogRunOperationAsync(
|
||||
ExportAuditOperation.RunCancelled,
|
||||
@@ -555,7 +556,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -582,7 +583,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -607,7 +608,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -940,7 +941,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -984,7 +985,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
@@ -1013,7 +1014,7 @@ public static class ExportApiEndpoints
|
||||
{
|
||||
var tenantId = GetTenantId(user);
|
||||
if (tenantId == Guid.Empty)
|
||||
return TypedResults.BadRequest("Tenant ID not found in claims");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_not_found_in_claims"));
|
||||
|
||||
var run = await runRepo.GetByIdAsync(tenantId, runId, cancellationToken);
|
||||
if (run is null)
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.Attestation;
|
||||
|
||||
@@ -72,7 +73,7 @@ public static class PromotionAttestationEndpoints
|
||||
var tenantId = ResolveTenantId(tenantIdHeader, httpContext);
|
||||
if (string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
return TypedResults.BadRequest("Tenant ID is required");
|
||||
return TypedResults.BadRequest(_t("exportcenter.validation.tenant_required"));
|
||||
}
|
||||
|
||||
// Ensure request has tenant ID
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.ExportCenter.Client.Models;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.AuditBundle;
|
||||
|
||||
@@ -124,7 +125,7 @@ public static class AuditBundleEndpoints
|
||||
|
||||
if (status.Status != "Completed")
|
||||
{
|
||||
return TypedResults.Conflict($"Bundle is not ready for download. Current status: {status.Status}");
|
||||
return TypedResults.Conflict(_t("exportcenter.error.bundle_not_ready", status.Status));
|
||||
}
|
||||
|
||||
var content = await handler.GetBundleContentAsync(bundleId, cancellationToken);
|
||||
|
||||
@@ -7,6 +7,7 @@ using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.Policy.Exceptions.Models;
|
||||
using StellaOps.Policy.Exceptions.Repositories;
|
||||
using System.Security.Claims;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.ExceptionReport;
|
||||
|
||||
@@ -56,7 +57,7 @@ public static class ExceptionReportEndpoints
|
||||
var tenantId = GetTenantId(user, context);
|
||||
if (tenantId is null)
|
||||
{
|
||||
return Results.BadRequest(new { error = "Tenant ID required" });
|
||||
return Results.BadRequest(new { error = _t("exportcenter.validation.tenant_required") });
|
||||
}
|
||||
|
||||
var requesterId = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? "unknown";
|
||||
@@ -95,7 +96,7 @@ public static class ExceptionReportEndpoints
|
||||
var tenantId = GetTenantId(user, context);
|
||||
if (tenantId is null)
|
||||
{
|
||||
return Results.BadRequest(new { error = "Tenant ID required" });
|
||||
return Results.BadRequest(new { error = _t("exportcenter.validation.tenant_required") });
|
||||
}
|
||||
|
||||
var reports = await generator.ListReportsAsync(tenantId.Value, limit ?? 50, cancellationToken);
|
||||
@@ -119,7 +120,7 @@ public static class ExceptionReportEndpoints
|
||||
var content = await generator.GetReportContentAsync(jobId, cancellationToken);
|
||||
if (content is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "Report not found or not ready" });
|
||||
return Results.NotFound(new { error = _t("exportcenter.error.report_not_found_or_not_ready") });
|
||||
}
|
||||
|
||||
return Results.File(
|
||||
|
||||
@@ -18,6 +18,7 @@ using StellaOps.ExportCenter.WebService.RiskBundle;
|
||||
using StellaOps.ExportCenter.WebService.SimulationExport;
|
||||
using StellaOps.ExportCenter.WebService.Telemetry;
|
||||
using StellaOps.ExportCenter.WebService.Timeline;
|
||||
using StellaOps.Localization;
|
||||
using StellaOps.Router.AspNet;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
@@ -107,6 +108,8 @@ builder.Services.AddOpenApi();
|
||||
|
||||
builder.Services.AddStellaOpsTenantServices();
|
||||
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
|
||||
builder.Services.AddStellaOpsLocalization(builder.Configuration);
|
||||
builder.Services.AddTranslationBundle(System.Reflection.Assembly.GetExecutingAssembly());
|
||||
|
||||
// Stella Router integration
|
||||
var routerEnabled = builder.Services.AddRouterMicroservice(
|
||||
@@ -125,6 +128,7 @@ if (app.Environment.IsDevelopment())
|
||||
}
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
app.UseStellaOpsLocalization();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseStellaOpsTenantMiddleware();
|
||||
@@ -185,6 +189,7 @@ app.MapDelete("/exports/{id}", (string id) => Results.NoContent())
|
||||
// Refresh Router endpoint cache
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
|
||||
await app.LoadTranslationsAsync();
|
||||
app.Run();
|
||||
|
||||
// Make Program class accessible for integration testing
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using static StellaOps.Localization.T;
|
||||
|
||||
namespace StellaOps.ExportCenter.WebService.RiskBundle;
|
||||
|
||||
@@ -125,7 +126,7 @@ public static class RiskBundleEndpoints
|
||||
|
||||
if (status.Status is not (RiskBundleJobStatus.Pending or RiskBundleJobStatus.Running))
|
||||
{
|
||||
return TypedResults.Conflict($"Job cannot be cancelled in status '{status.Status}'");
|
||||
return TypedResults.Conflict(_t("exportcenter.error.job_cancel_invalid_status", status.Status));
|
||||
}
|
||||
|
||||
var actor = httpContext.User.FindFirst("sub")?.Value
|
||||
@@ -134,7 +135,7 @@ public static class RiskBundleEndpoints
|
||||
var cancelled = await handler.CancelJobAsync(jobId, actor, cancellationToken);
|
||||
if (!cancelled)
|
||||
{
|
||||
return TypedResults.Conflict("Failed to cancel job");
|
||||
return TypedResults.Conflict(_t("exportcenter.error.job_cancel_failed"));
|
||||
}
|
||||
|
||||
return TypedResults.NoContent();
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<ProjectReference Include="..\..\..\Policy\StellaOps.Policy.Engine\StellaOps.Policy.Engine.csproj" />
|
||||
<ProjectReference Include="..\..\..\Router/__Libraries/StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="..\..\..\Attestor\__Libraries\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Localization\StellaOps.Localization.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Translations\*.json" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="StellaOpsReleaseVersion">
|
||||
<Version>1.0.0-alpha1</Version>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"_meta": { "locale": "en-US", "namespace": "exportcenter", "version": "1.0" },
|
||||
|
||||
"exportcenter.error.report_not_found_or_not_ready": "Report not found or not ready.",
|
||||
"exportcenter.error.bundle_not_ready": "Bundle is not ready for download. Current status: {0}.",
|
||||
"exportcenter.error.job_cancel_invalid_status": "Job cannot be cancelled in status '{0}'.",
|
||||
"exportcenter.error.job_cancel_failed": "Failed to cancel job.",
|
||||
"exportcenter.error.profile_name_conflict": "Profile name '{0}' already exists.",
|
||||
"exportcenter.error.profile_archived": "Cannot update archived profile.",
|
||||
"exportcenter.error.profile_not_active": "Profile is not active.",
|
||||
"exportcenter.error.run_cancel_invalid_state": "Run cannot be cancelled in its current state.",
|
||||
|
||||
"exportcenter.validation.tenant_required": "Tenant ID is required.",
|
||||
"exportcenter.validation.tenant_not_found_in_claims": "Tenant ID not found in claims."
|
||||
}
|
||||
Reference in New Issue
Block a user